簡體   English   中英

為什么 Rust 編譯器需要 Option<&impl Trait> 的類型注釋?

[英]Why does the rust compiler require a type annotation for Option<&impl Trait>?

鑒於此 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 )的類型的任何具體實例對編譯器來說都是可以的。


邊注:
我已經閱讀了有關&dyn Trait&impl Trait之間區別的答案 我不確定何時使用 which,但由於我的程序確實使用&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"

這個:

fn foo(maybe_trait: Option<&impl Trait>) -> String {

只是語法糖:

fn foo<T: Trait>(maybe_trait: Option<&T>) -> String {

這意味着編譯器將生成許多foo函數,每個T (實現Trait類型)一個。 因此,即使您使用None調用它,編譯器也需要知道在這種情況下哪個是T ,以便它可以選擇/生成正確的函數。

Option<T>類型在內存中的表示方式取決於T類型的表示方式。 函數foo的編譯程序集依賴foo 對於不同的T ,生成的組件可能看起來不同。 (例如,定義是Some還是None的 enum 標簽可能處於不同的字節偏移量。它可能使用不同的寄存器,它可能會以不同的方式決定是否展開循環、內聯函數、向量化……)這就是靜態分派 - 即使您編寫了大量抽象的代碼,您也可以獲得針對您實際使用的具體類型完全優化的代碼。

使用即將推出的專業化功能,您實際上可以為T不同子集手動編寫不同的foo實現,因此編譯器知道您正在調用哪個foo非常重要。 每個人都可以用None做一些不同的事情。


另一方面這個:

fn foo(maybe_trait: Option<&dyn Trait>) -> String {

意味着只有一個函數foo接受 Option ,該函數包含一個指向實現Trait某種類型的胖指針。 如果您在maybe_trait內部調用maybe_trait上的某個方法,該調用將通過動態調度。

因為只有一個函數foo ,所以在使用None時你不必說任何關於類型的信息,只有一個。

但是動態調度是有代價的——這個函數沒有針對任何特定的T進行優化,它動態地與每個T

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM