...the compiler rejects it with the following error:
error[E0308]: mismatched types
--> src/lib.rs:17:20
|
13 | impl<T: Foo + 'static> FooStorage
| - this type parameter
...
17 | Arc::clone(&self.foo)
| ^^^^^^^^^ expected trait object `dyn Foo`, found type parameter `T`
|
= note: expected reference `&std::sync::Arc<dyn Foo>`
found reference `&std::sync::Arc<T>`
= help: type parameters must be constrained to match other types
= note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
I found that it compiles if I use Arc::<T>::clone(...) or <Arc<T> as Clone>::clone(...) but I would like to understand why the type inference succeeds if a binding is introduced and immediately returned.
I think this is because of the interaction between method resolution and type inference. "Method resolution" refers to the compiler's algorithm for taking a short-hand syntax like foo.clone() or Arc::clone(&foo) and mapping it to a specific clone method (e.g. <Arc<T> as Clone>::clone).
Method resolution in Rust as I understand it is fairly simplistic: Starting from the types known at the call site, the compiler searches for a method with the correct name, and stops searching as soon as it finds one. It always chooses the first method in its search order, even if that one fails type-check while some later method would pass.
I believe method resolution (unlike type inference) also works in a single forward pass through the code: Each method call is resolved based on type information from the code up to that point; it can't use types that are inferred from code that comes later in the function.
So in the original code, apparently it finds <Arc<dyn Foo> as Clone>::clone first, because it knows at this point that the expected return type is Arc<dyn Foo>. In the modified code, the expected type of the ret binding isn't known at this point, so the method resolver has to start from the argument type, &Arc<T>, so it correctly finds <Arc<T> as Clone>::clone.