Eri*_*aas 6 wrapper rust invariance
引用包装类一样&Rc<T>,并&Box<T>在不变的T(&Rc<T>是不是&Rc<U>就算T是U).问题的一个具体例子(Rust Playground):
use std::rc::Rc;
use std::rc::Weak;
trait MyTrait {}
struct MyStruct {
}
impl MyTrait for MyStruct {}
fn foo(rc_trait: Weak<MyTrait>) {}
fn main() {
let a = Rc::new(MyStruct {});
foo(Rc::downgrade(&a));
}
Run Code Online (Sandbox Code Playgroud)
此代码导致以下错误:
<anon>:15:23: 15:25 error: mismatched types:
expected `&alloc::rc::Rc<MyTrait>`,
found `&alloc::rc::Rc<MyStruct>`
Run Code Online (Sandbox Code Playgroud)
类似的例子(有类似的错误)与Box<T>(Rust Playground):
trait MyTrait {}
struct MyStruct {
}
impl MyTrait for MyStruct {}
fn foo(rc_trait: &Box<MyTrait>) {}
fn main() {
let a = Box::new(MyStruct {});
foo(&a);
}
Run Code Online (Sandbox Code Playgroud)
在这些情况下,我当然可以使用a所需的类型进行注释,但在许多情况下,这是不可能的,因为还需要原始类型.那我该怎么办呢?
您在此处看到的内容与差异和子类型完全无关。
首先,关于 Rust中子类型的最有用的读物是Nomicon 的这一章。您会发现在 Rust 子类型关系中(即,当您可以将一种类型的值传递给需要不同类型变量的函数或变量时)是非常有限的。它只能在您处理生命周期时观察到。
例如,下面的一段代码显示了&Box<T>(co)variant 的准确程度:
fn test<'a>(x: &'a Box<&'a i32>) {}
fn main() {
static X: i32 = 12;
let xr: &'static i32 = &X;
let xb: Box<&'static i32> = Box::new(xr); // <---- start of box lifetime
let xbr: &Box<&'static i32> = &xb;
test(xbr); // Covariance in action: since 'static is longer than or the
// same as any 'a, &Box<&'static i32> can be passed to
// a function which expects &'a Box<&'a i32>
//
// Note that it is important that both "inner" and "outer"
// references in the function signature are defined with
// the same lifetime parameter, and thus in `test(xbr)` call
// 'a gets instantiated with the lifetime associated with
// the scope I've marked with <----, but nevertheless we are
// able to pass &'static i32 as &'a i32 because the
// aforementioned scope is less than 'static, therefore any
// shared reference type with 'static lifetime is a subtype of
// a reference type with the lifetime of that scope
} // <---- end of box lifetime
Run Code Online (Sandbox Code Playgroud)
这个程序会编译,这意味着&和Box在它们各自的类型和生命周期参数上是协变的。
与大多数具有 C++ 和 Java 等类/接口的“传统”OOP 语言不同,Rust 中的特征不引入子类型关系。尽管说,
trait Show {
fn show(&self) -> String;
}
Run Code Online (Sandbox Code Playgroud)
非常相似
interface Show {
String show();
}
Run Code Online (Sandbox Code Playgroud)
在某些语言(如 Java)中,它们在语义上完全不同。在 Rust 裸 trait 中,当用作类型时,永远不是实现此 trait 的任何类型的超类型:
impl Show for i32 { ... }
// the above does not mean that i32 <: Show
Run Code Online (Sandbox Code Playgroud)
Show, 虽然是一个 trait, 确实可以用在类型 position, 但它表示一种特殊的unsized 类型, 只能用于形成trait objects。你不能拥有裸特征类型的值,因此谈论裸特征类型的子类型和变化甚至没有意义。
Trait 对象采用&SomeTraitor 或&mut SomeTraitor 的形式SmartPointer<SomeTrait>,它们可以被传递并存储在变量中,并且需要它们来抽象出 trait 的实际实现。但是,&TwhereT: SomeTrait 不是的子类型&SomeTrait,并且这些类型根本不参与方差。
Trait 对象和常规指针具有不兼容的内部结构:&T只是一个指向具体类型的常规指针T,&SomeTrait而是一个胖指针,它包含一个指向实现的类型的原始值的SomeTrait指针,以及一个指向 vtable 的第二个指针,用于实现SomeTrait属于上述类型。
传递&Tas&SomeTrait或Rc<T>asRc<SomeTrait>工作的事实是因为 Rust对引用和智能指针进行自动强制:如果它知道,它能够&SomeTrait为常规引用构造一个胖指针;我相信这是很自然的。例如,您的示例有效,因为返回一个类型的值,该值被强制转换为.&TTRc::downgrade()Rc::downgrade()Weak<MyStruct>Weak<MyTrait>
然而,&Box<SomeTrait>用&Box<T>if构造T: SomeTrait要复杂得多:一方面,编译器需要分配一个新的临时值,因为Box<T>和Box<SomeTrait>具有不同的内存表示。如果你有,比如说Box<Box<T>>,让Box<Box<SomeTrait>>出它是更复杂,因为它需要建立在堆中分配新存储Box<SomeTrait>。因此,没有对嵌套引用和智能指针的自动强制,同样,这与子类型和变化完全没有关系。
| 归档时间: |
|
| 查看次数: |
326 次 |
| 最近记录: |