【发布时间】:2019-11-23 21:22:09
【问题描述】:
鉴于此 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();
}
rust 编译器不高兴:
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`.
使用类型注释可以编译:
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();
}
如果我们在类型注解中使用Trait 而不是Struct,我们会得到更多信息:
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`.
我将其理解为“您不应在此处使用特征,因为那时我不知道需要为该参数分配多少内存”。
但是当我通过 None 时,为什么这有关系?
当然,传递实现Trait(即Struct)的类型的任何具体实例对编译器来说都是可以的。
旁注:
我已经阅读了this answer 关于&dyn Trait 和&impl Trait 之间的区别。我不确定何时使用哪个,但由于我的程序确实使用 &impl Trait 编译(当使用上述类型注释时),它似乎是安全的选择。
如果我们将函数参数设为Option<&dyn Trait> 类型,我的程序将在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可以是Option<Struct>::None或Option::<AnotherTypeImplementingTrait>::None。参数位置的impl trait与泛型一样工作,因此编译器必须知道要实例化的foo的版本,None可以是任何东西。 -
这很重要,Option 的大小取决于实现 trait 的类型。
-
我认为你错过了
foo::HugeThing的行为可能与foo::OtherThing不同 即使通过None,编译器必须知道使用正确的. -
(此外,并非所有引用的大小都相同,因为对未指定大小类型的引用是胖指针,但即使是胖指针,编译器仍然不会为您选择类型。)
标签: rust