读完the subtyping chapter of the Nomicon ,我无法理解类型参数的协方差。特别是对于 Box<T>
类型,描述为:T is covariant
.
但是,如果我写这段代码:
trait A {}
trait B: A {}
struct C;
impl A for C {}
impl B for C {}
fn foo(v: Box<dyn A>) {}
fn main() {
let c = C;
let b: Box<dyn B> = Box::new(c);
foo(b);
}
( Playground )
error[E0308]: mismatched types
--> src/main.rs:13:9
|
13 | foo(b);
| ^ expected trait `A`, found trait `B`
|
= note: expected type `std::boxed::Box<(dyn A + 'static)>`
found type `std::boxed::Box<dyn B>`
B
显然是 A
的“子类型”和 Box
在其输入上是协变的。我不知道为什么它不起作用或者为什么它不会进行任何类型的强制转换。为什么他们会考虑 Box<T>
在唯一用例是不变量的情况下是协变的?
最佳答案
Rust 中的子类型和变体意味着什么
Nomicon 并不是一份完全完善的文件。现在,该 repo 协议(protocol)中最近的 10 期中有 5 期专门根据其标题单独处理子类型或差异。 Nomicon 中的概念可能需要大量努力,但信息通常就在那里。
首先,检查一些初始段落(强调我的):
Subtyping in Rust is a bit different from subtyping in other languages. This makes it harder to give simple examples, which is a problem since subtyping, and especially variance, are already hard to understand properly.
To keep things simple, this section will consider a small extension to the Rust language that adds a new and simpler subtyping relationship. After establishing concepts and issues under this simpler system, we will then relate it back to how subtyping actually occurs in Rust.
然后它继续显示一些基于特征的代码。重申一下,这段代码不再是 Rust 代码;特征在 Rust 中不形成子类型!
后来,有这句话:
First and foremost, subtyping references based on their lifetimes is the entire point of subtyping in Rust. The only reason we have subtyping is so we can pass long-lived things where short-lived things are expected.
Rust 的子类型概念仅适用于生命周期。
子类型化和方差的例子是什么?
变体生命周期
这是一个在 Box
中工作的子类型和生命周期变化的例子.
一个失败的案例
fn smaller<'a>(v: Box<&'a i32>) {
bigger(v)
}
fn bigger(v: Box<&'static i32>) {}
error[E0308]: mismatched types
--> src/lib.rs:2:12
|
2 | bigger(v)
| ^ lifetime mismatch
|
= note: expected type `std::boxed::Box<&'static i32>`
found type `std::boxed::Box<&'a i32>`
note: the lifetime 'a as defined on the function body at 1:12...
--> src/lib.rs:1:12
|
1 | fn smaller<'a>(v: Box<&'a i32>) {
| ^^
= note: ...does not necessarily outlive the static lifetime
一个工作案例
fn smaller<'a>(v: Box<&'a i32>) {}
fn bigger(v: Box<&'static i32>) {
smaller(v)
}
不变的生命周期
这是一个有效的案例:
struct S<'a>(&'a i32);
fn smaller<'a>(_v: &S<'a>, _x: &'a i32) {}
fn bigger(v: &S<'static>) {
let x: i32 = 1;
smaller(v, &x);
}
将所有引用更改为可变引用的相同代码将失败,因为可变引用是不变的:
struct S<'a>(&'a mut i32);
fn smaller<'a>(_v: &mut S<'a>, _x: &'a mut i32) {}
fn bigger(v: &mut S<'static>) {
let mut x: i32 = 1;
smaller(v, &mut x);
}
error[E0597]: `x` does not live long enough
--> src/lib.rs:7:16
|
7 | smaller(v, &mut x);
| -----------^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `x` is borrowed for `'static`
8 | }
| - `x` dropped here while still borrowed
解决具体问题
B
is clearly a "subtype" ofA
不是。
Box
is covariant over its input
是的,协方差仅适用于生命周期。
I don't know why it doesn't work or why it won't do any type coercion.
Why doesn't Rust support trait object upcasting? 涵盖了这一点
Why would they consider
Box<T>
to be covariant
因为它是,对于 Rust 中应用了方差的东西。
另见
关于rust - 如果 Box<dyn B> 不是 Box<dyn A> 的子类型,其中 B : A?,这意味着 Box 是协变的是什么意思,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55200843/