您在这里看到的内容与方差和子类型根本无关。
首先,有关 Rust 子类型的信息最丰富的读物是本章 https://doc.rust-lang.org/nightly/nomicon/subtyping.html诺米康。您可以在 Rust 子类型关系中找到这一点(即,当您可以将一种类型的值传递给需要以下变量的函数或变量时)不同的类型)非常有限。只有当你使用生命周期时才能观察到它。
例如,下面的代码显示了如何准确地&Box<T>
是(共同)变体:
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
该程序可以编译,这意味着两者&
and Box
在它们各自的类型和生命周期参数上是协变的。
与大多数具有 C++ 和 Java 等类/接口的“传统”OOP 语言不同,Rust 特征不引入子类型关系。尽管说,
trait Show {
fn show(&self) -> String;
}
高度相似
interface Show {
String show();
}
在像Java这样的某些语言中,它们在语义上有很大不同。在 Rust 中,当用作类型时,裸特征是never实现此特征的任何类型的超类型:
impl Show for i32 { ... }
// the above does not mean that i32 <: Show
Show
,虽然是一种特质,但确实can用于类型位置,但它表示一个特殊的无尺寸类型只能用于形成特质对象 http://doc.rust-lang.org/book/trait-objects.html。您不能拥有裸特征类型的值,因此谈论裸特征类型的子类型和方差甚至没有意义。
特征对象的形式为&SomeTrait
or &mut SomeTrait
or SmartPointer<SomeTrait>
, 和他们can被传递并存储在变量中,并且需要它们来抽象特征的实际实现。然而,&T
where T: SomeTrait
is not的一个子类型&SomeTrait
,并且这些类型根本不参与方差。
Trait 对象和常规指针具有不兼容的内部结构:&T
只是一个指向具体类型的常规指针T
, while &SomeTrait
是一个胖指针,其中包含指向实现的类型的原始值的指针SomeTrait
还有第二个指向 vtable 的指针,用于实现SomeTrait
上述类型的。
事实是经过&T
as &SomeTrait
or Rc<T>
as Rc<SomeTrait>
工作之所以发生,是因为 Rust 确实如此自动强制对于引用和智能指针:它能够构造一个胖指针&SomeTrait
供定期参考&T
如果它知道T
;我相信这是很自然的。例如,你的例子Rc::downgrade()
有效是因为Rc::downgrade()
返回类型的值Weak<MyStruct>
这被强迫Weak<MyTrait>
.
然而,构建&Box<SomeTrait>
out of &Box<T>
if T: SomeTrait
更为复杂:首先,编译器需要分配一个new临时值,因为Box<T>
and Box<SomeTrait>
有不同的记忆表示。如果你有,请说,Box<Box<T>>
,得到Box<Box<SomeTrait>>
它甚至更复杂,因为它需要在堆上创建新的分配储藏Box<SomeTrait>
。因此,嵌套引用和智能指针没有自动强制,而且这与子类型和方差根本没有关系。