Given this MCVE:
fn main() {
println!("{}", foo(None));
}
trait Trait {}
struct Struct {}
impl Trait for Struct {}
fn foo(maybe_trait: Option<&impl Trait>) -> String {
return "hello".to_string();
}
The rust compiler is not happy:
error[E0282]: type annotations needed
--> src\main.rs:2:20
|
2 | println!("{}", foo(None));
| ^^^ cannot infer type for `impl Trait`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0282`.
Using type annotations makes this compile:
fn main() {
let nothing: Option<&Struct> = None;
println!("{}", foo(nothing));
}
trait Trait {}
struct Struct {}
impl Trait for Struct {}
fn foo(maybe_trait: Option<&impl Trait>) -> String {
return "hello".to_string();
}
If we use Trait
instead of Struct
in the type annotation, there is a bit more information given to us:
warning: trait objects without an explicit `dyn` are deprecated
--> src\main.rs:2:26
|
2 | let nothing: Option<&Trait> = None;
| ^^^^^ help: use `dyn`: `dyn Trait`
|
= note: #[warn(bare_trait_objects)] on by default
error[E0277]: the size for values of type `dyn Trait` cannot be known at compilation time
--> src\main.rs:3:20
|
3 | println!("{}", foo(nothing));
| ^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `dyn Trait`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
note: required by `foo`
--> src\main.rs:10:1
|
10| fn foo(maybe_trait: Option<&impl Trait>) -> String {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
I understand this as "You shall not use a trait here, because then I do not know how much memory I need to allocate for this parameter".
But why is that relevant when I am passing None
?
Of course, passing any concrete instance of a type implementing Trait
(i.e. Struct
) is okay to the compiler.
Sidenote:
I have read this answer on the difference between &dyn Trait
and &impl Trait
. I'm unsure when to use which, but since my program does compile with &impl Trait
(when using type annotations as above) it seems like the safe choice.
If instead we make the function parameter be of type Option<&dyn Trait>
, my program compiles without type annotations within main()
:
fn main() {
println!("{}", foo(None));
}
trait Trait {}
struct Struct {}
impl Trait for Struct {}
fn foo(maybe_trait: Option<&dyn Trait>) -> String {
return "hello".to_string();
}
$ cargo --version
cargo 1.37.0 (9edd08916 2019-08-02)
$ cat Cargo.toml
[package]
name = "rdbug"
version = "0.1.0"
authors = ["redacted"]
edition = "2018"
None
could beOption<Struct>::None
orOption::<AnotherTypeImplementingTrait>::None
.impl trait
in argument position works like generics, so the compiler has to know what version offoo
to instantiate, andNone
could be anything. – trentclfoo::HugeThing
might behave differently thanfoo::OtherThing
even when passedNone
, and the compiler has to know to use the right one. – trentcl