为什么不能将结构分配给具有它实现的特征的绑定

Ren*_*ato 7 rust

我试图理解 Rust 多态性。从我的 OOP 背景来看,我希望以下 Rust 代码能够工作:

use std::io::{stdin, Read};

fn main() {
    let r: Read = stdin();
    println!("ok");
}
Run Code Online (Sandbox Code Playgroud)

但它没有:

use std::io::{stdin, Read};

fn main() {
    let r: Read = stdin();
    println!("ok");
}
Run Code Online (Sandbox Code Playgroud)

我知道有一个Readimpl for StdIn,那么我怎样才能使这个(或任何正确的方法来做到这一点)工作,即。use Stdin,或 a File,或什至是 aString如果可能(无法找到实现)在Read预期a 的地方使用?

我不认为我可以在这里使用泛型,因为我需要将 的实例(r可以是任何实现的Read)传递给另一个方法,但如果我错了,请告诉我。

Luk*_*odt 6

您可能想在这里使用特征对象。您基本上可以通过两种方式使用特征:

  • 静态调度方式:fn foo<T: Trait>(x: T). 这读作“对于一个任意但固定的 T实现Trait”。
  • 动态调度方式:fn foo(x: &Trait). 这通过使用特征对象从第一个版本中解除了“但固定”的限制。

如果您想要一个变量,该变量可以保存对Stdin或 aFile或任何其他实现的引用Read,则不能采用静态调度解决方案,因为您的实现类型不是固定的。


那么你的变量的类型是什么?Read? 可悲的是,这并不容易。性状对象是未上浆的,并且不能在堆栈上直接使用。相反,你只能与交互特征通过引用/指针,就像对象&Read&mut ReadBox<Read>等等。现在我们使用了借用东西,我们可能会遇到更多与此相关的问题。幸运的是,您并不是第一个遇到此问题的人:请参阅此问题此特定用例的详细信息,。

为了简化一点,在大多数情况下可以使用Box<Trait>. 在您的情况下,它看起来像这样:

use std::io::{stdin, Read};

fn main() {
    let r: Box<Read> = Box::new(stdin());
    println!("ok");
}
Run Code Online (Sandbox Code Playgroud)

  • @Renato Trait 对象使用所谓的 *vtables* 来实现运行时多态性。这些虚表包含指向所有特征方法的函数指针!因此,您无需手动执行此操作。此外,LLVM 知道 vtable 并可能执行优化。所以鼓励使用 trait 对象。关于你的 `String` 问题:使用实现 `Read` 的 [`Cursor&lt;String&gt;`](https://doc.rust-lang.org/stable/std/io/struct.Cursor.html)。 (2认同)