How can I avoid "value assigned to last is never read" warning from this macro?
from veer66@lemmy.one to rust@programming.dev on 13 Mar 2024 01:08
https://lemmy.one/post/11809909

Removing last will break my library.

#[macro_export]
macro_rules! list {
    () => {
	None
    };
    [ $x:expr, $( $y:expr ),* ] => {
	{
	    let mut first = cons($x, &None);
	    let mut last = &mut first;
	    $(
		let yet_another = cons($y, &None);
		if let Some(ref mut last_inner) = last {
		    let last_mut = Rc::get_mut(last_inner).unwrap();
		    last_mut.cdr = yet_another;
		    last = &mut last_mut.cdr;
		}
	    )*
	    first
	}
    }
}

This macro works as I expected because it can pass these tests.

    #[test]
    fn dolist() {
        let mut v = vec![];
        dolist!((i &cons(10, &list![20, 30, 40])) {
            v.push(i.car);
        });
        assert_eq!(v, vec![10, 20, 30, 40]);
    }

    #[test]
    fn turn_list_to_vec() {
        assert_eq!(list_to_vec(&list![1, 2, 3]), vec![1, 2, 3]);
    }

    #[test]
    fn count_elements() {
        assert_eq!(list_len(&list![10, 20, 30]), 3);
    }

However I got the warning “value assigned to last is never read.”

How can I avoid this warning?

P.S. Full code

#rust

threaded - newest

Solemarc@lemmy.world on 13 Mar 2024 01:29 next collapse

I’m not sure if the rules are different with macros, I’ve never written one but this lint is generally caused because you set a var to a value and then overwrite that value before you use it. e.g.

let mut a = 1; a = 2; println!(“{}”, a);

This will throw the same warning because 1 is never used, this could’ve just been:

let a = 2; println!(“{}”, a);

So first I’d double check that I NEED last at all. Maybe try:

cargo clippy

See if it can tell you how to fix it.

If that doesn’t work, it’s sometimes necessary to skip certain lints. E.g. if you make a library, most of the code will be flagged as dead code because it isn’t used and you can use an #[allow(dead_code)] to stop the linter warning. You might be able to use #[allow(this_linting_rule)].

Hope something here helps.

veer66@lemmy.one on 13 Mar 2024 02:04 collapse

Clippy didn’t tell anything about the macro.

warning: dereferencing a tuple pattern where every element takes a reference
  --> src/lib.rs:13:9
   |
13 |         &Some(ref cons_rc) => {
   |         ^^^^^^^^^^^^^^^^^^
   |
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrowed_reference
   = note: `#[warn(clippy::needless_borrowed_reference)]` on by default
help: try removing the `&` and `ref` parts
   |
13 -         &Some(ref cons_rc) => {
13 +         Some(cons_rc) => {
   |

To put #[allow(this_linting_rule)] like this:

    [ $x:expr, $( $y:expr ),* ] => {
	#[allow(unused_assignments)]
	{

I got error[E0658]: attributes on expressions are experimental.

To put it like this:

#[macro_export]
#[allow(unused_assignments)]
macro_rules! list {
    () => {
	None

It doesn’t work.

v9CYKjLeia10dZpz88iU@programming.dev on 13 Mar 2024 02:10 collapse

I would expect the following to work

#[allow(unused)]
let mut last = &mut first;
veer66@lemmy.one on 13 Mar 2024 02:17 collapse

It doesn’t work, at least, on rustc 1.75.

v9CYKjLeia10dZpz88iU@programming.dev on 13 Mar 2024 02:40 collapse

I misunderstood the reason the error was showing up. It seems like using a closure fixes it though.

play.rust-lang.org/?version=stable&mode=debug&edi…

#[macro_export]
macro_rules! list {
    () => {
        None
    };
    [ $x:expr, $( $y:expr ),* ] => {{
        #[allow(unused_assignments)]
        let closure = move || {
            let mut first = cons($x, &None);
            let mut last = &mut first;

            $(
                let yet_another = cons($y, &None);
                if let Some(ref mut last_inner) = last {
                    let last_mut = Rc::get_mut(last_inner).unwrap();
                    last_mut.cdr = yet_another;

                    last = &mut last_mut.cdr;
                }
            )*

            first
        };

        closure()
    }}
}
veer66@lemmy.one on 13 Mar 2024 02:44 collapse

  #[allow(unused_assignments)]

Thank you. This works!

5C5C5C@programming.dev on 13 Mar 2024 02:53 next collapse

If you put an underscore as the first character in the variable name, that tells the compiler that the variable may go unused and you’re okay with that. E.g.

let mut _last = &mut first;
v9CYKjLeia10dZpz88iU@programming.dev on 13 Mar 2024 02:59 next collapse

The issue is related to the binding being reassigned then never used. It’s happening because they’re using code generation where the final loop doesn’t need to assign. (but previous loops use the assignment at the loop start. Just throwing all that in a function and adding the warning suppression fixes it.

edit: I’m wrong, using an underscore works

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=2ccac2a848523cb995b6c0efd92c569c

sugar_in_your_tea@sh.itjust.works on 13 Mar 2024 04:27 collapse

That’s a neat trick!

I knew it worked for params, but never thought to use it for variables.

[deleted] on 13 Mar 2024 04:12 collapse

.