1 Subtyping and Variance #
参考:
Subtyping 是隐式操作(类似的隐式操作还有 type coercion), 用于 type checking 和 infrerence, 它们都指的是 lifetime 之间的关系(Rust 的自定义类型之间没有 subtype 语义),包含两种类型:
- 生命周期更长的 lifetime 是更短的子类型:
- ‘b: ‘a 表示 ‘b 的生命周期比 ‘a 长,在需要 ‘a 的地方都可以传入 ‘b;
- ‘static 的生命周期比其它的都长,在需要 ‘a 的地方,可以传入 ‘static;
- HRTB 的 lifetime 是其它任意 lifetime 的子类型:
注:supertrait 用于约束 Self 必须实现多个 trait + ’lifetime, 并不表示 trait 之间的 subtype 关系。
// HRTB 支持闭包
fn use_closure<F>(f: F) where F: for <'a> Fn(&'a str) -> &'a str,
{
let s = "example";
let result = f(s); // f 的 HRBT lifetime 是任意其它 lifetime 的子类型
println!("{}", result);
}
// HRTB 支持函数指针
fn use_fn_ptr(f: for<'a> fn(&'a str) -> &'a str) {
let s = "example";
let result = f(s); // f 的 HRBT lifetime 是任意其他 lifetime 的子类型
println!("{}", result);
}
let subtype: &(for<'a> fn(&'a i32) -> &'a i32) = &((|x| x) as fn(&_) -> &_);
let supertype: &(fn(&'static i32) -> &'static i32) = subtype;
let subtype: &(dyn for<'a> Fn(&'a i32) -> &'a i32) = &|x| x;
let supertype: &(dyn Fn(&'static i32) -> &'static i32) = subtype;
let subtype: &(for<'a, 'b> fn(&'a i32, &'b i32))= &((|x, y| {}) as fn(&_, &_));
let supertype: &for<'c> fn(&'c i32, &'c i32) = subtype;
Variance
指在传参和赋值时,类型之间如果存在 subtype 关系,则 Rust 隐式自动转换,例如 T 是 U 的子类型 ,则 T 的值会被自动转换为 U 的值:
- covariant 协变: 如果 T 是 U 的 subtype, 则 F<T> 是 F<U> 的子类型;
- contravariant 逆变: 如果 T 是 U 的 subtype, 则 F<U> 是 F<T> 的子类型;
- invariant 不可变: 没有子类型的关系;
Rust 支持的 Variance 列表:
Type | Variance in ‘a | Variance in T |
---|---|---|
&‘a T | covariant, ‘a 可以有子类型 | covariant,T 可以有子类型 |
&‘a mut T | covariant, ‘a 可以有子类型 | invariant, T 不可变 |
*const T | covariant,T 可以有子类型 | |
*mut T | invariant | |
[T] and [T; N] | covariant, T 可以有子类型 | |
fn() -> T | covariant, T 可以有子类型 | |
fn(T) -> () | contravariant, 可以传入 T 的父类型 | |
std::cell::UnsafeCell<T> | invariant,T 不可变 | |
std::marker::PhantomData<T> | covariant,T 可以有子类型 | |
dyn Trait<T> + ‘a | covariant, ‘a 可以有子类型 | invariant, T 不可以变 |
- T 代表一个类型,实际可能包含 lifetime, 比如 fn() -> T 中的 T 可能是 &‘a String,
这时可以使用返回 &‘static String 的函数指针, 因为 ‘static 是 ‘a 的子类型;
- fn(T) -> () 中的 T 是逆变,对于 fn(&‘static String) 则可以传入 fn(&‘a String)。
- Vec<T> 等容器类型,以及 Box<T> 类型,都是 covariant,但 Cell<T> 是 invariant;
- UnsafeCell<T>/Cell<T> 等内部可变性类型和 &mut T 类型一致,T 不可变。
示例:
- A
&'long T
coerces to a&'short T
:另外,T 也可以协变到 U; - A
&'long mut T
coerces to a&'short mut T
:虽然 T 不可变,但 lifetime 可变; - A
&'medium &'long U
coerces to a&'short &'short U
- A
&'medium mut &'long mut U
coerces to a&'short mut &'long mut U
…, but not to a &‘short mut &‘short mut U Cell<T>
andRefCell<T>
are also invariant in T.T: dyn Trait<U> 或 T: Trait<U>
, U becomes invariant because it’s a type parameter of the trait. If your U resolves to&'x V
, the lifetime'x
will be invariant too.
// 由于输入参数是 &mut T 类型,所以在赋值时 val 对应 T 的类型是不可变的,必须和 input 的 T 类型一
// 致。
fn assign<T>(input: &mut T, val: T) {
*input = val;
}
fn main() {
let mut hello: &'static str = "hello";
{
let world = String::from("world");
// 错误:传入的参数类型是 &mut &'static str,所以 T 的类型必须是 &'static str, 但是 &world
// 的类型是 &'world str,所以编译错误。
assign(&mut hello, &world);
}
println!("{hello}");
}
// error[E0597]: `world` does not live long enough
// --> src/main.rs:9:28
// |
// 6 | let mut hello: &'static str = "hello";
// | ------------ type annotation requires that `world` is borrowed for `'static`
// ...
// 9 | assign(&mut hello, &world);
// | ^^^^^^ borrowed value does not live long enough
// 10 | }
// | - `world` dropped here while still borrowed
// Box<T> 的 T是可以协变的,&'static 是 &'b 的 subtype,所以可以赋值:
let hello: Box<&'static str> = Box::new("hello");
let mut world: Box<&'b str>;
// 对象所有权转移时可以更改可变性
world = hello;
fn get_str() -> &'a str;
fn get_static() -> &'static str;
thread_local! {
pub static StaticVecs: RefCell<Vec<&'static str>> = RefCell::new(Vec::new());
}
fn store(input: &'static str) {
StaticVecs.with_borrow_mut(|v| v.push(input));
}
fn demo<'a>(input: &'a str, f: fn(&'a str)) {
f(input);
}
fn main() {
// "hello" 是 'static 的, 所以 store() 可以使用它
demo("hello", store);
{
let smuggle = String::from("smuggle");
// 错误: `fn(&'static str)` 不是 `fn(&'a str)` 的子类型.(函数指针是反向协变)
demo(&smuggle, store);
}
}
自定义 struct/enum/union 的 variance 取决于各 field 的 variance:
- 如果 parameter A 在各 field 的使用都是 covariant, 则整体对 A 是 covariant;
- 如果 parameter A 在各 field 的使用都是 contravariant, 则整体对 A 是 contravariant;
- 否则,整体对 A 是 invariant;
如下面的 struct 的 ‘a 和 T 是 covariant 可变的, 而 ‘b, ‘c 和 U 是 invariant 不可变的:
use std::cell::UnsafeCell;
struct Variance<'a, 'b, 'c, T, U: 'a> {
x: &'a U, // This makes `Variance` covariant in 'a, and would make it covariant
// in U, but U is used later, 由于 U 在多个 field 中使用, 所以 U 是不
// 可变的
y: *const T, // Covariant in T
z: UnsafeCell<&'b f64>, // Invariant in 'b, 因为 std::cell::UnsafeCell<T> 中的 T 是不可变的.
w: *mut U, // Invariant in U, makes the whole struct invariant, 部分字段不可变时,
// 整个 struct 不可变.
f: fn(&'c ()) -> &'c () // Both co- and contravariant, makes 'c invariant in the struct.
}
在 struct/enum/uion field 之外, variance 是单独计算的, 长的 lifetime 类型值可以赋值给短的 liftime 类型值:
fn generic_tuple<'short, 'long: 'short>(
// 'long is used inside of a tuple in both a co- and invariant position.
x: (&'long u32, UnsafeCell<&'long u32>),
) {
// 在析构时赋值时,各变量 variance 单独计算
let _: (&'short u32, UnsafeCell<&'long u32>) = x;
}
fn takes_fn_ptr<'short, 'middle: 'short>(
f: fn(&'middle ()) -> &'middle (),
) {
// 各变量 variance 单独计算: 函数输入参数是逆变,返回结果是协变。
let _: fn(&'static ()) -> &'short () = f;
}
2 type-coercions #
type-coercions 是 Rust 的隐式行为,用于改变一个 value 的 type,Rust 只在一些特定的位置(场景)才会使用类型协变。
在函数传参匹配 trait bound 时不会进行协变,例如虽然 &mut i32 可以协变到 &i32, 但传参时不会协变。带来的影响是:如果 Trait 作为函数参数限界,&i32 和 &mut i32 两种类型都需要实现该 Trait。
trait Trait {}
fn foo<X: Trait>(t: X) {}
impl<'a> Trait for &'a i32 {}
fn main() {
let t: &mut i32 = &mut 0;
foo(t);
}
// error[E0277]: the trait bound `&mut i32: Trait` is not satisfied
// --> src/main.rs:9:9
// |
// 3 | fn foo<X: Trait>(t: X) {}
// | ----- required by this bound in `foo`
// ...
// 9 | foo(t);
// | ^ the trait `Trait` is not implemented for `&mut i32`
// |
// = help: the following implementations were found:
// <&'a i32 as Trait>
// = note: `Trait` is implemented for `&i32`, but not for `&mut i32`
Rust 隐式类型协变的场景(也可以使用 as 运算符进行显式转换):
-
let 赋值语句, 如
let _: &i8 = &mut 42;
-
static 和 const 常量声明;
-
函数调用时的传参;
fn bar(_: &i8) { } fn main() { bar(&mut 42); }
-
实例化 struct/unio/enum variant field 时;
struct Foo<'a> { x: &'a i8 } fn main() { Foo { x: &mut 42 }; }
-
函数返回值:
use std::fmt::Display; fn foo(x: &u32) -> &dyn Display { x }
另外还有一些协变传播表达式(coercion-propagating expression
), 如:
- Array 字面量;
- Tuples
- 括起来的子表达式 ((e))
- Blocks
3 Coercion types #
Coercion is allowed between the following types:
-
T to U if T is a
subtype
of U (reflexive case
) -
T_1 to T_3 where T_1 coerces to T_2 and T_2 coerces to T_3 (
transitive case
) -
&mut T to &T
-
*mut T to *const T
-
&T to *const T
-
&mut T to *mut T
-
&T or &mut T to &U
if T implementsDeref<Target = U>
-
&mut T to &mut U
if T implementsDerefMut<Target = U>
-
TyCtor(T) to TyCtor(U), where TyCtor(T) is one of
- &T
- &mut T
- *const T
- *mut T
- Box<T>
and where U can be obtained from T by
unsized coercion
. -
Function
item types tofn pointers
-
Non capturing closures
tofn pointers
-
! to any T
:如 match 的某个条件返回 return
use std::ops::Deref;
struct CharContainer {
value: char,
}
impl Deref for CharContainer {
type Target = char;
fn deref<'a>(&'a self) -> &'a char {
&self.value
}
}
fn foo(arg: &char) {}
fn main() {
let x = &mut CharContainer { value: 'y' };
foo(x); //&mut CharContainer 被协变到 &char.
}
// 将 &mut T 转换为 *mut T
let ptr: *mut i32 = &mut 0;
let ref_transmuted = unsafe {
std::mem::transmute::<*mut i32, &mut i32>(ptr)
};
// Use a reborrow instead
let ref_casted = unsafe { &mut *ptr };
// 将 &mut T 转换为 &mut U:
let ptr = &mut 0;
let val_transmuted = unsafe {
std::mem::transmute::<&mut i32, &mut u32>(ptr)
};
let val_casts = unsafe { &mut *(ptr as *mut i32 as *mut u32) };
4 Unsized Coercions #
unsized coercions 指的是将 sized 类型转换为 unsized 类型,它们是编译器定义的特殊 隐式转换行为
。
unsized coercions 的实现与 Unsize and CoerceUnsized
trait 相关, =Unsize trait 只能由编译器内置实现=,而 CoerceUnsized 是可以为自定义类型实现的 trait。
以下是编译器内置的 unsized 协变,即如果编译器为 T 实现了 Unsize<U>,则 T 可以 unsized 协变到 U:
[T; n] to [T]
T to dyn U
:当 T 实现了 U + Sized, 且 U 是 object safe. <- T 必须是 Sized- Foo<…, T, …> 到 Foo<…, U, …>, 当:
- Foo 是 struct 类型;
- T 实现了 Unsize<U>
- Foo 的最后一个 field 使用了 T 类型;
- 如果 Foo field 有 Bar<T> 类型,则 Bar<T> 需要实现 Unsized<Bar<U>>;
- T 没有在 Foo 的其它 field 中使用;
当 T 实现了 Unsize<U> 或 CoerceUnsized<Foo<U>> 时,Foo<T> 实现 CoerceUnsized<Foo<U>>
。
5 Unsized trait #
Unsized trait 是一个 marker trait, 只能由编译器自动实现, 主要用到的是两种:
- 数组 [T; N] 实现了 Unsize<[T]>, 这意味着 &[T;N] 可以赋值给 &[T] 类型;
- trait object: 如果 value 实现了 MyTrait, 则可以将
value
赋值给dyn MyTrait
;
对于内置类型,如果 T: Unsize<U>, 则 &T 可以自动隐式转换为 &U。
// https://doc.rust-lang.org/std/marker/trait.Unsize.html
pub trait Unsize<T> where T: ?Sized, { }
let u: &dyn MyTrait = &value;
let u: Box<dyn MyTrait> = Box::new(value);
Unsize trait 和 CoerceUnsized trait 结合使用,可以允许自定义类型,如 Rc,包含动态大小的类型。
6 CoerceUnsized trait #
对于自定义类型,如果满足 Foo<T>: CoerceUnsized<Foo<U>>,则 Foo<T> 可以被自动隐式转换为 Foo<U>.
// https://doc.rust-lang.org/std/ops/trait.CoerceUnsized.html
pub trait CoerceUnsized<T> where T: ?Sized, { }
CoerceUnsized 是 marker trait,提供指针类型(含智能指针)如 &T/&mut T/*const T/*mut T/Box<T>/Rc<T>/Arc<T> 以及包装器类型,如 Cell<T>/RefCell<T>/Pin<T>,到另一个 unsized 类型 U 的 unsized coerce:
T 和 U 都是 ?Sized
;- &T 可以 unsized coerce 到 &U 或 *const U;
- &mut T 可以 unsized coerce 到 &mut U 或 *mut U 或 &U 或 *const U;
- 裸指针间 unsized 转换;
- 智能指针间转换,如 Box<T>/Rc<T>/Arc<T> 到 Box<U>/Rc<U>/Arc<U>;
- 智能指针的包装器类型间转换,如 Cell/Pin<Box<T>> 到 Cell/Pin<Box<U>>;
// &T 可以 unsized coerce 到 &U 或 *const U
impl<'a, 'b, T, U> CoerceUnsized<&'a U> for &'b T where 'b: 'a, T: Unsize<U> + ?Sized, U: ?Sized
impl<'a, T, U> CoerceUnsized<*const U> for &'a T where T: Unsize<U> + ?Sized, U: ?Sized
// &mut T 可以 unsized coerce 到 &mut U 或 *mut U 或 &U 或 *const U
impl<'a, T, U> CoerceUnsized<*mut U> for &'a mut T where T: Unsize<U> + ?Sized, U: ?Sized,
impl<'a, T, U> CoerceUnsized<&'a mut U> for &'a mut T where T: Unsize<U> + ?Sized, U: ?Sized,
impl<'a, 'b, T, U> CoerceUnsized<&'a U> for &'b mut T where 'b: 'a, T: Unsize<U> + ?Sized, U: ?Sized,
impl<'a, T, U> CoerceUnsized<*const U> for &'a mut T where T: Unsize<U> + ?Sized, U: ?Sized,
// 裸指针间转换
impl<T, U> CoerceUnsized<*const U> for *const T where T: Unsize<U> + ?Sized, U: ?Sized,
impl<T, U> CoerceUnsized<*const U> for *mut T where T: Unsize<U> + ?Sized, U: ?Sized,
impl<T, U> CoerceUnsized<*mut U> for *mut T where T: Unsize<U> + ?Sized, U: ?Sized,
// 智能指针间转换,如 Box<T> 到 Box<U>
// Ref 是 RefCell.borrow() 返回的类型, RefMut 是 RefCell.borrow_mut() 返回的类型;
impl<'b, T, U> CoerceUnsized<Ref<'b, U>> for Ref<'b, T> where T: Unsize<U> + ?Sized, U: ?Sized,
impl<'b, T, U> CoerceUnsized<RefMut<'b, U>> for RefMut<'b, T> where T: Unsize<U> + ?Sized, U: ?Sized,
impl<T, U, A> CoerceUnsized<Box<U, A>> for Box<T, A> where T: Unsize<U> + ?Sized, A: Allocator, U: ?Sized,
impl<T, U, A> CoerceUnsized<Rc<U, A>> for Rc<T, A> where T: Unsize<U> + ?Sized, A: Allocator, U: ?Sized,
impl<T, U, A> CoerceUnsized<Arc<U, A>> for Arc<T, A> where T: Unsize<U> + ?Sized, A: Allocator, U: ?Sized,
impl<T, U> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> + ?Sized, U: ?Sized,
// 智能指针的包装器类型,如 Cell<Box<T>> 到 Cell<Box<U>>.
impl<Ptr, U> CoerceUnsized<Pin<U>> for Pin<Ptr> where Ptr: CoerceUnsized<U>,
impl<T, U> CoerceUnsized<Cell<U>> for Cell<T> where T: CoerceUnsized<U>,
impl<T, U> CoerceUnsized<RefCell<U>> for RefCell<T> where T: CoerceUnsized<U>,
impl<T, U> CoerceUnsized<SyncUnsafeCell<U>> for SyncUnsafeCell<T> where T: CoerceUnsized<U>,
impl<T, U> CoerceUnsized<UnsafeCell<U>> for UnsafeCell<T> where T: CoerceUnsized<U>,
impl<T, U, A> CoerceUnsized<Weak<U, A>> for std::rc::Weak<T, A> where T: Unsize<U> + ?Sized, A: Allocator, U: ?Sized
impl<T, U, A> CoerceUnsized<Weak<U, A>> for std::sync::Weak<T, A> where T: Unsize<U> + ?Sized, A: Allocator, U: ?Sized
Option 没有实现 CoerceUnsized,所以不能实现 Option<T> 到 Option<U> 的转换:
trait X {
fn x(&mut self);
}
struct XX;
impl X for XX {
fn x(&mut self) {
todo!()
}
}
struct C<'a> {
mut_ref: Option<&'a mut dyn X>,
}
fn main() {
// Box <dyn X > 等效于 Box <dyn X + 'static>,所以 b 的实际类型是 Option<Box<dyn X + 'static>>
let mut b: Option<Box<dyn X>> = Some(Box::new(XX));
// as_deref_mut() 的签名:
//
// pub fn as_deref_mut(&mut self) -> Option<&mut <T as Deref>::Target> where T: DerefMut
//
// 所以 b.as_deref_mut() 的结果类型是 Option<&'a mut(dyn X + 'static)>.
// 重点来了: Option<T> 是不支持协变到 Option<U> 的, 所以 Option<&'a mut(dyn X + 'static)> 不能协
// 变到 mut_ref 要求的 Option<&'a mut(dyn X + 'a).
// match 匹配 Some(x) 之所以 OK, 是因为 x 此时的类型已经是 &'a mut(dyn X + 'static) 了,而标准库
// 实现了 impl<'a, T, U> CoerceUnsized<&'a mut U> for &'a mut T where T: Unsize<U> + ?Sized, U:
// ?Sized,当 T 是 unsized 的 dyn X + 'static 且 U 是 dyn X + 'a 时, 满足上面的 where 约束, 所以
// 支持 &'a mut(dyn X + 'static) 到 &'a mut(dyn X + 'a) 的 unsized 协变, 所以下面的 match 表达式
// 可以执行成功.
// error[E0597]: `b` does not live long enough
// let c = C {
// mut_ref: b.as_deref_mut(),
// };
let c = match b.as_deref_mut() {
None => C { mut_ref: None },
Some(x) => C { mut_ref: Some(x) },
};
}
使用 CoerceUnsized trait 实现的 type coercion 转换的例子:
// 满足 CoerceUnsized<Ptr<U>> for Ptr<T> where T: Unsize<U> + ?Sized 时,
// Ptr<T> 可以 type coercion 到 Ptr<U>, 这里的 Ptr 类型是:
//
// &'b T 或 &'b mut T 或 Ref<'b, T> 或 RefMut<'b, T> 或 *mut T 或 *const T 或 Cell<T> 或 RefCell<T>或 Box<T, A> 或 Rc<T, A> 或 Arc<T, A>,
//
// 但不包含 Result 或 Option
// [T; N] -> [T]
let bo: Box<[i32]> = Box::new([1, 2, 3]);
// 1i32 实现了 dyn std::fmt::Display 和 dyn std::any::Any, 所以 1i32 可以 unsized coercion 到
// trait object trait object 有两种形式1. &dyn Trait
let dsd: &dyn std::fmt::Display = &1i32;
// 2. Box<dyn Trait>
let bo: Box<dyn std::fmt::Display> = Box::new(1i32);
let bo: Box<dyn std::any::Any> = Box::new(1i32);
let bo: Box<&dyn std::fmt::Display> = Box::new(&1i32);
// 以下也是 CoerceUnsized trait 实现的 type coercion 的转换. U 需要是 unsized type.
// &T -> &U
let u: &dyn std::fmt::Display = &123i32;
// &mut T -> &U
let u: &dyn std::fmt::Display = &mut 123i32;
// &mut T -> &mut U
let u: &mut dyn std::fmt::Display = &mut 123i32;
// &T -> *const U
let u: *const dyn std::fmt::Display = &123i32;
// &mut T -> *const U
let u: *const dyn std::fmt::Display = &mut 123i32;
// &mut T -> *mut U
let u: *mut dyn std::fmt::Display = &mut 123i32;
7 Least upper bound coercions #
In some contexts, the compiler must coerce together multiple types
to try and find the most general
type. This is called a "Least Upper Bound" coercion
.
LUB coercion is used and only used in the following situations:
- To find
the common type
for a series of if branches. - To find the common type for a series of match arms.
- To find the common type for array elements.
- To find the type for the return type of a closure with multiple return statements.
- To check the type for the return type of a function with multiple return statements.
In each such case, there are a set of types T0..Tn to be mutually coerced to some target type T_t,
which is unknown to start. Computing the LUB coercion is done iteratively
. The target type T_t
begins as the type T0. For each new type Ti, we consider whether
- If Ti can be coerced to the current target type T_t, then no change is made.
- Otherwise, check whether T_t can be coerced to Ti; if so, the T_t is changed to Ti. (This check is also conditioned on whether all of the source expressions considered thus far have implicit coercions.)
- If not, try to compute a mutual supertype of T_t and Ti, which will become the new target type.
Examples:
// For if branches
let bar = if true {
a
} else if false {
b
} else {
c
};
// For match arms
let baw = match 42 {
0 => a,
1 => b,
_ => c,
};
// For array elements
let bax = [a, b, c];
// For closure with multiple return statements
let clo = || {
if true {
a
} else if false {
b
} else {
c
}
};
let baz = clo();
// For type checking of function with multiple return statements
fn foo() -> i32 {
let (a, b, c) = (0, 1, 2);
match 42 {
0 => a,
1 => b,
_ => c,
}
}
In these examples, types of the ba* are found by LUB coercion
. And the compiler checks whether LUB
coercion result of a, b, c is i32 in the processing of the function foo.
Caveat: This description is obviously informal. Making it more precise is expected to proceed as part of a general effort to specify the Rust type checker more precisely.