Unnecessary parentheses?

I was going through the Beginning-Rust pdf, and found this example:

    let factor = 2;
    let multiply = |a| a * factor;
    let multiply_ref: (&Fn(i32) -> i32) = &multiply;

This piece of code will tell you (In VSCode at least), that there's "Unnecessary parentheses around type".

So, I rewrite my code like this:

    let factor = 2;
    let multiply = |a| a * factor;
    let multiply_ref: &Fn(i32) -> i32 = &multiply;

Then I get a different warning like this: "trait objects without an explicit dyn are deprecated".

How is the appropiate way of writing the line?

let multiply_ref: &Fn(i32) -> i32 = &multiply;

And, what is that thing about the traits?

Thanks in advance, I know I'm asking too much xD.

According to the playground the warning message is:

warning: trait objects without an explicit `dyn` are deprecated
 --> src/main.rs:4:24
  |
4 |     let multiply_ref: &Fn(i32) -> i32 = &multiply;
  |                        ^^^^^^^^^^^^^^ help: use `dyn`: `dyn Fn(i32) -> i32`
  |
  = note: `#[warn(bare_trait_objects)]` on by default

Fn(...) -> ... is a trait, and traits are not type. To use the trait object with dynamic dispatch you should attach the dyn keyword before the trait name. So this should work without warning.

    let factor = 2;
    let multiply = |a| a * factor;
    let multiply_ref: &dyn Fn(i32) -> i32 = &multiply;
6 Likes

To generalize @Hyeonu‘s solution a bit: If your code has multiple errors (or warnings) in it, the compiler will often be able to spot only one of them at a time.

When you make a change to fix one error and get a brand-new error back, it’s usually a good idea to press forward with trying to fix the new error without reversing your original change. If your proposed fix didn’t work, the original error will eventually show up again on its own.

The chain of errors can sometimes be long, but there’s a correct program at the end of it somewhere.

6 Likes

Wow, thanks. With that simple statement it finally clicks in my mind what that "dyn" thing is all about.

1 Like

In this case the compiler outputs both warnings. The IDE probably wasn't able to show the second warning for some reason.

2 Likes

This seems oddly common, unfortunately. I've seen a bunch of cases on Discord where the fix for a problem was "compile in the terminal so you can see the awesome error that rustc is generating rather than just the bit the IDE is showing".

2 Likes

The dyn keyword thing is attached there because Rust can't know the size at compile time, right?

No -- str is also a DST, but doesn't allow dyn.

You can think of dyn as "an operator that turns a trait into a type". That resulting type will be a DST, but it's not the DST-ness that's why it needed dyn.

2 Likes

Here's what I got from what you said:
dyn will turn the Trait into a Dynamically Sized Type, or, as you said DST, when used in conjunction with a trait it will tell Rust that the type doesn't have a compile time known size.

I understood most of what you said, but the last part seems weird to me.
"but it's not the DST-ness that's why it needed dyn."

If A is a trait (and meets certain requirements for "object safety"), then dyn A is a type. This type can hold any value of any T such that T: A. But -- conceptually -- as soon as you turn a value of type T into a value of type dyn A, the compiler no longer tracks the original type T. So given a value of type dyn A, it's not possible statically (= at compile time) to know its size, because A could be implemented by two types of different sizes, and the compiler doesn't know which of these types the value came from. A value of type dyn A is called a "trait object". Because the amount of memory needed to store a given dyn A value isn't known at compile time, Rust will not allocate such values on the stack (there's a way to do this but it's not currently exposed by the language), and in particular you can't have a variable of type dyn A. Instead, you have to store the dyn A value on the heap and keep a pointer of some kind (shared or exclusive reference, Box, Rc, ...).

dyn is only used in this context, to describe trait object types. Other types are like dyn A in that their values don't have a fixed statically-known size, but are not trait object types. An example (as @scottmcm said) is str, which can represent string data of arbitrary size. And as with dyn A, we must use indirection to work with values of these "unsized" types (so you can't have a variable of type str, you have to use &str or Box<str> or etc.).

In summary, dyn A where A is an object-safe trait is always an unsized type, but some unsized types are not of the form dyn A for any A, like str or [u8].

4 Likes

The dyn keyword refers to dynamic dispatch rather than dynamic sizing (though in Rust, the two are somewhat related).

4 Likes