4.5.3.3 Trait 约束交互
译文 · 基于 Learning Rust
Trait 约束交互
原文链接: https://quinedot.github.io/rust-learning/dyn-elision-trait-bounds.html
当 trait 本身有生命周期约束时,这些约束可能影响 dyn Trait 生命周期省略的行为。影响发生的位置和方式没有适当文档,但我们会在此涵盖一些情况。
这些场景中 trait 对象生命周期默认行为不直观,或许甚至任意。但明确说,你可能永远不需要知道省略时的确切规则。带奇特生命周期约束的 trait 很罕见,若真的遇到,通常可以选择显式写出而非猜测默认生命周期。
也就是说,本小节更多是对编译器当前行为的探索,而非实用学习内容。若在学习实用 Rust,可能应该跳过。
非常高层的摘要:
- trait 约束在 trait 对象生命周期上引入隐式约束
- 存在非
'static trait 生命周期约束时,省略行为任意,最好显式写出 - 建议不要为自己的对象安全 trait 添加非
'static 生命周期约束
本节也不详尽。鉴于我遇到的例外很多,请对我本节中的断言持保留态度。
trait 生命周期约束创建隐式约束
trait 约束在 dyn Trait 生命周期上创建隐式约束:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| pub trait LifetimeTrait<'a, 'b>: 'a {}
pub fn f<'b>(_: Box<dyn LifetimeTrait<'_, 'b> + 'b>) {}
fn fp<'a, 'b, 'c>(t: Box<dyn LifetimeTrait<'a, 'b> + 'c>) {
// 能编译,表明隐式 `'c: 'a` 约束
let c: &'c [()] = &[];
let _: &'a [()] = c;
// 不能,表明未隐含 `'c: 'b`
// (即隐式约束仅针对 trait 对象生命周期,
// 而非其他参数。)
//let _: &'b [()] = c;
// 不能,因为需要 `'c: 'b` 和 `'b: 'a`
//f(t);
}
|
类似 &'b &'a _ 创建隐式 'a: 'b 约束。仅适用于 trait 对象生命周期,而非整个 dyn Trait(例如不适用于 trait 参数)。
'static 情况
我们已在基本准则中总结 trait 本身有 'static 约束时 trait 对象生命周期省略的行为:此情况下生命周期总是 'static。
这甚至适用于
- 有多个生命周期约束的歧义类型
- 有单一生命周期约束如
&_ 的类型- 即 trait 对象生命周期(为
'static)与外层生命周期独立
- 某些下面例子中非
'static 约束未覆盖 &_ trait 对象生命周期默认的情况
即使多个约束中只有一个为 'static,此情况也适用,与结构体定义中视为歧义的约束对比。
单一 trait 生命周期约束并非总是适用
根据参考手册,在无生命周期约束的泛型结构体上下文中,有单一生命周期约束的 trait 的默认 trait 对象生命周期总是 trait 约束中的生命周期。
话很多,但含义是:
1
| trait Single<'a>: 'a {}
|
Box<dyn Single<'a>> 的省略生命周期总是 'a。
但实际并非如此:
1
2
3
4
5
| # trait Single<'a>: 'a {}
// 省略的生命周期是 `'static` 而非 `'a`,因此可编译
fn foo<'a>(s: Box<dyn Single<'a>>) {
let s: Box<dyn Single<'a> + 'static> = s;
}
|
1
2
3
4
5
| # trait Single<'a>: 'a {}
// 此情况下*是* `'a`,因此编译失败
fn bar<'a: 'a>(s: Box<dyn Single<'a>>) {
let s: Box<dyn Single<'a> + 'static> = s;
}
|
适用时,trait 生命周期约束覆盖结构体约束
根据参考手册,trait 上的约束永不覆盖结构体上的约束。但根据我的测试,相反:当 trait 约束适用时,它们总是覆盖结构体约束。
复杂部分是弄清何时适用。
例如以下可编译,但根据参考手册应因结构体上多个生命周期约束而歧义。没有 trait 上的生命周期约束则不能编译;trait 约束覆盖了结构体上的歧义约束。
1
2
3
4
5
6
7
8
9
10
11
| use core::marker::PhantomData;
// 去掉 `: 'a` 可看编译错误
pub trait LifetimeTrait<'a>: 'a {}
pub struct Over<'a, T: 'a + 'static + ?Sized>(&'a T);
pub struct Invariant<T: ?Sized>(*mut PhantomData<T>);
unsafe impl<T: ?Sized> Sync for Invariant<T> {}
pub static OS: Invariant<Over<'_, dyn LifetimeTrait>> = Invariant(std::ptr::null_mut());
|
下面有些例子 trait 约束也覆盖 &_ 约束,因此不仅是歧义结构体约束可被 trait 约束覆盖。
多个 trait 约束可能歧义或可能适用
以下因 trait 上多个生命周期约束被视为歧义。
1
2
3
4
5
6
7
| trait Double<'a, 'b>: 'a + 'b {}
fn f<'a, 'b, T: Double<'a, 'b> + 'static>(t: T) {
let bx: Box<dyn Double<'a, 'b>> = Box::new(t);
// 此版本有效:
let bx: Box<dyn Double<'a, 'b> + 'static> = Box::new(t);
}
|
当前文档对此沉默,但多约束 trait 仍可能以提供默认 trait 对象生命周期的方式适用。
1
2
3
4
5
6
7
8
9
10
11
12
| pub trait Double<'a, 'b>: 'a + 'b {}
fn x1<'a: 'a, 'b>(bx: Box<dyn Double<'a, 'b>>) {
// 失败(生命周期不是 `'static`)
//let bx: Box<dyn Double<'a, 'b> + 'static> = bx;
// 也失败(生命周期不是 `'b` 也不是 `'a + 'b`)
//let bx: Box<dyn Double<'a, 'b> + 'b> = bx;
// 但成功,可断定生命周期是 `'a`
let bx: Box<dyn Double<'a, 'b> + 'a> = bx;
}
|
微妙之处:省略的 trait 对象生命周期是 'a,但由于 trait 约束,trait 对象生命周期上有隐式 : 'a + 'b 约束。因此函数签名有隐式 'a: 'b 约束,类似 &'b &'a _ 参数。
trait 约束在函数体中总是适用
根据测试,dyn Trait 注解在函数体中的默认 trait 对象生命周期总是 trait 约束。事实上,此约束甚至覆盖通配符 '_ 生命周期注解。
这是对 '_ 注解恢复「普通」生命周期省略行为的惊人例外。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| trait Single<'a>: 'a {}
fn baz<'long: 'a, 'a, T: 'long + Single<'a>>(s: T) {
// 末尾赋值可编译:
//let s: Box<dyn Single<'a> + 'long> = Box::new(s);
// 但以下都不能编译,因为 `'a: 'long` 不成立:
//let s: Box<dyn Single<'a>> = Box::new(s);
//let s: Box<dyn Single<'_>> = Box::new(s);
//let s: Box<dyn Single<'a> + '_> = Box::new(s);
//let s: Box<dyn Single<'_> + '_> = Box::new(s);
//let s: Box<dyn Single + '_> = Box::new(s);
let s: Box<dyn Single> = Box::new(s);
let s: Box<dyn Single<'_> + 'long> = s;
}
|
trait 生命周期约束何时以及如何适用?
看过许多例子后,可以推测何时以及如何适用。例子已说明,不同上下文规则非常不同。
在函数签名中
这似乎是 trait 对象生命周期省略最复杂、最任意的上下文。
若仔细注意,可能注意到例子中偶尔有 'a: 'a 等平凡约束,影响 trait 约束是否适用。没有显式约束的函数生命周期参数称为晚绑定参数,生命周期是否晚绑定影响 trait 约束在函数签名中何时适用。 非晚绑定的参数是早绑定。
称 trait 中同时是 trait 约束边界的生命周期参数为「边界参数」。我对行为的假设如下:
- 若任何 trait 约束是
'static,默认生命周期是 'static - 若任何边界参数显式为
'static,默认生命周期是 'static - 若恰好一个边界参数是早绑定,默认生命周期是该生命周期
- 包括在多个位置,如
dyn Double<'a, 'a>
- 若多于一个边界参数是早绑定,默认生命周期歧义
- 若没有边界参数是早绑定,默认生命周期取决于
struct 约束(与无约束 trait 相同)
任何情况下,由于 trait 约束而在 trait 对象生命周期上的隐式约束仍然有效。
要求恰好一个边界参数早绑定或任一为 'static 是语法要求,而非语义。例如:
1
2
3
4
5
6
7
8
9
| pub trait Double<'a, 'b>: 'a + 'b {}
// 语义上 `'a` 和 `'b` 必须是 `'static`。但参数未显式 `'static`,
// 因此此 trait 对象生命周期被视为歧义(尽管由于隐式约束也必须是 `'static`)。
fn foo<'a: 'static, 'b: 'static>(d: Box<dyn Double<'a, 'b>>) {}
// 语义上 `'a` 和 `'b` 必须相同。由于约束也是早绑定参数。
// 但参数语法上不是同一生命周期,因此 trait 对象生命周期歧义。
fn bar<'a: 'b, 'b: 'a>(d: &dyn Double<'a, 'b>) {}
|
但若将任一例子改为 Double<'a, 'a>,则恰好一个边界参数早绑定,将可编译:
1
2
3
| # pub trait Double<'a, 'b>: 'a + 'b {}
fn foo<'a: 'static, 'b: 'static>(d: Box<dyn Double<'a, 'a>>) {}
fn bar<'a: 'b, 'b: 'a>(d: &dyn Double<'a, 'a>) {}
|
隐式约束不否定晚绑定
考虑 &dyn Trait 时,外层引用生命周期与 dyn Trait 之间总有隐式约束(除 trait 本身的隐式约束外)。但这些隐式约束不足以单独使 trait 约束适用。即使有隐式约束,生命周期仍可以是晚绑定。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
| pub trait LifetimeTrait<'a>: 'a {}
impl LifetimeTrait<'_> for () {}
// 以下都能与下面 `fp` 函数一起编译,表明 trait 约束确实适用,
// 导致 trait 对象生命周期独立于引用生命周期
pub fn f<'a: 'a>(_: &dyn LifetimeTrait<'a>) {}
//pub fn f<'a: 'a>(_: &'_ dyn LifetimeTrait<'a>) {}
//pub fn f<'r, 'a: 'a>(_: &'r dyn LifetimeTrait<'a>) {}
//pub fn f<'r: 'r, 'a: 'a>(_: &'r dyn LifetimeTrait<'a>) {}
//pub fn f<'r, 'a: 'r + 'a>(_: &'r dyn LifetimeTrait<'a>) {}
//pub fn f<'r: 'r, 'a: 'r>(_: &'r dyn LifetimeTrait<'a>) {}
// 但以下都不能与 `fp` 一起编译,表明省略的 trait 对象生命周期
// 按「正常」默认为引用生命周期。
//pub fn f(_: &dyn LifetimeTrait) {}
//pub fn f(_: &'_ dyn LifetimeTrait) {}
//pub fn f<'r>(_: &'r dyn LifetimeTrait) {}
//pub fn f<'r: 'r>(_: &'r dyn LifetimeTrait) {}
//pub fn f(_: &dyn LifetimeTrait<'_>) {}
//pub fn f(_: &'_ dyn LifetimeTrait<'_>) {}
//pub fn f<'r>(_: &'r dyn LifetimeTrait<'_>) {}
//pub fn f<'r: 'r>(_: &'r dyn LifetimeTrait<'_>) {}
//pub fn f<'a>(_: &dyn LifetimeTrait<'a>) {}
//pub fn f<'a>(_: &'_ dyn LifetimeTrait<'a>) {}
//pub fn f<'r, 'a>(_: &'r dyn LifetimeTrait<'a>) {}
//pub fn f<'r: 'r, 'a>(_: &'r dyn LifetimeTrait<'a>) {}
// 注:`'a` 因是 trait 参数而不变
fn fp<'a>(t: &(dyn LifetimeTrait<'a> + 'a)) {
f(t);
}
|
上述例子也表明 trait 约束适用时,确实覆盖非歧义结构体约束(如 &_)。
隐式约束与默认对象约束交互
给定签名的默认对象生命周期可能与隐式约束以令人意外的方式交互。考虑:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| # pub trait LifetimeTrait<'a>: 'a {}
// `&'outer (dyn Lifetime<'param> + 'trait)` 中的隐式约束:
// - `'param: 'outer`(引用有效性)
// - `'trait: 'outer`(引用有效性)
// - `'trait: 'param`(来自 trait 约束)
//
// 由于此情况下省略参数不适用 trait 约束,还有 `'outer = 'trait`
// 由于 `&_` 的「正常」默认生命周期行为。将该等式加入上述约束
// 导致*三个生命周期必须相同*。
//
// 因此可编译:
pub fn g<'r, 'a>(d: &'r dyn LifetimeTrait<'a>) {
let r: [&'r (); 1] = [&()];
let a: [&'a (); 1] = [&()];
let _: [&'a (); 1] = r;
let _: [&'r (); 1] = a;
let _: &'r (dyn LifetimeTrait<'r> + 'r) = d;
let _: &'a (dyn LifetimeTrait<'a> + 'a) = d;
}
|
更复杂约束的结果更令人意外:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| trait Double<'a, 'b>: 'a + 'b {}
fn h<'a, 'b, T>(bx: Box<dyn Double<'a, 'b>>, t: &'a T)
where
&'a T: Send, // 这使 `'a` 早绑定
{
// 按上述规则 `bx` 是 `Box<dyn Double<'a, 'b> + 'a>`,
// 因此不能编译:
//let _: Box<dyn Double<'a, 'b> + 'static> = bx;
// 但隐式约束仍适用,意味着:
// - `'a: 'a + 'b`
// - 因此 `'a: 'b`
//
// 这就是为什么即使任何地方都没声明该约束也能编译!
let t: &'b T = t;
// 生命周期仍不相同,因此失败
let _: &'a T = t;
}
|
上述例子中 'a: 'b 是隐式约束的唯一原因是以下交互:
- trait 对象生命周期上的隐式
: 'a + 'b 约束 - 默认 trait 对象生命周期为
'a
若 'b 也早绑定,默认 trait 对象生命周期歧义。若 'a 不是早绑定,默认 trait 对象生命周期为 'static,没有隐式 'a: 'b 约束。
通配符生命周期仍引入新的推断生命周期
根据测试,使用 '_ 会像典型生命周期省略一样,在输入位置引入新的推断生命周期,在输出位置遵循函数签名省略规则。
高阶生命周期是晚绑定
根据测试,for<'a> dyn Trait... 生命周期与晚绑定生命周期行为相同。
函数体
如上所述,trait 对象约束在函数体中总是适用,类似函数签名中每个生命周期都早绑定。无论函数签名中生命周期是早绑定还是晚绑定都如此。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| trait Single<'a>: 'a {}
fn foo<'r, 'a>(bx: Box<dyn Single<'a> + 'static>, rf: &'r (dyn Single<'a> + 'static)) {
// 此处是 `'a`,不是 `'static` 也不是推断
let bx: Box<dyn Single<'a>> = bx;
// 因此失败
//let _: Box<dyn Single<'a> + 'static> = bx;
// 此处是 `'a`,与引用生命周期不同也不是推断
let a: &dyn Single<'a> = rf;
// 因此成功
let _: &(dyn Single<'a> + 'a) = a;
// 失败
//let _: &(dyn Single<'a> + 'static) = a;
// 引用生命周期显式时行为相同
let a: &'r dyn Single<'a> = rf;
let _: &'r (dyn Single<'a> + 'a) = a;
//let _: &'r (dyn Single<'a> + 'static) = a;
// 也失败,表明 `'r` 不是 `'a`
//let _: &'a &'r () = &&();
}
|
与其他地方不同,在函数体中用 '_ 代替完全省略 trait 对象生命周期不会恢复普通生命周期省略(即推断生命周期)。上面三个例子若使用 '_ 行为完全相同。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # trait Single<'a>: 'a {}
fn foo<'r, 'a>(bx: Box<dyn Single<'a> + 'static>, rf: &'r (dyn Single<'a> + 'static)) {
let bx: Box<dyn Single<'a> + '_> = bx;
// 失败
//let _: Box<dyn Single<'a> + 'static> = bx;
let a: &(dyn Single<'a> + '_) = rf;
let _: &(dyn Single<'a> + 'a) = a;
// 失败
//let _: &(dyn Single<'a> + 'static) = a;
let a: &'r (dyn Single<'a> + '_) = rf;
let _: &'r (dyn Single<'a> + 'a) = a;
// 失败
//let _: &'r (dyn Single<'a> + 'static) = a;
}
|
与函数签名行为结合,可能导致尴尬情况。
1
2
3
4
5
6
7
8
9
10
11
| trait Double<'a, 'b>: 'a + 'b {}
// 签名中 `'_` 像「正常」一样为 trait 对象生命周期创建独立生命周期;称 `'c`。
// 虽独立,但因隐式约束相关:`'c: 'a + 'b`
fn foo<'a, 'b>(bx: Box<dyn Double<'a, 'b> + '_>) {
// 函数体中默认 trait 对象生命周期被视为歧义,`'_` 不覆盖。
//
// 且无法命名 `'c`,因为签名中省略了。可注解为 `'a` 或 `'b`,
// 除非改变函数签名给生命周期命名,否则无法「保留」完整生命周期。
let bx: Box<dyn Double<'a, 'b> + '_> = bx;
}
|
static 上下文
在大多数 static 上下文中,任何省略的生命周期(不仅是 trait 对象生命周期)默认为 'static。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| # use core::marker::PhantomData;
trait Single<'a>: 'a + Send + Sync {}
trait Halfie<'a, 'b>: 'a + Send + Sync {}
trait Double<'a, 'b>: 'a + 'b + Send + Sync {}
static BS: PhantomData<Box<dyn Single<'_>>> = PhantomData;
static BH: PhantomData<Box<dyn Halfie<'_, '_>>> = PhantomData;
static BD: PhantomData<Box<dyn Double<'_, '_>>> = PhantomData;
static S_BS: PhantomData<Box<dyn Single<'static> + 'static>> = BS;
static S_BH: PhantomData<Box<dyn Halfie<'static, 'static> + 'static>> = BH;
static S_BD: PhantomData<Box<dyn Double<'static, 'static> + 'static>> = BD;
const CS: PhantomData<Box<dyn Single<'_>>> = PhantomData;
const CH: PhantomData<Box<dyn Halfie<'_, '_>>> = PhantomData;
const CD: PhantomData<Box<dyn Double<'_, '_>>> = PhantomData;
const S_CS: PhantomData<Box<dyn Single<'static> + 'static>> = CS;
const S_CH: PhantomData<Box<dyn Halfie<'static, 'static> + 'static>> = CH;
const S_CD: PhantomData<Box<dyn Double<'static, 'static> + 'static>> = CD;
|
但从 Rust 1.64 起,关联 const 允许使用一般省略生命周期和通配符生命周期(而不仅是省略 trait 对象生命周期)。这是意外稳定化,可能移除或修改。
与此同时,省略的生命周期像 impl 块上的独立生命周期变量。它们又像函数签名中的早绑定生命周期。
1
2
3
4
5
6
7
8
9
10
| # use core::marker::PhantomData;
# trait Single<'a>: 'a + Send + Sync {}
struct L<'l, 'm>(&'l str, &'m str);
impl<'a, 'b> L<'a, 'b> {
const CS: PhantomData<Box<dyn Single<'a>>> = PhantomData;
// 失败
//const S_CS: PhantomData<Box<dyn Single<'a> + 'static>> = Self::CS;
const S_CS: PhantomData<Box<dyn Single<'a> + 'a>> = Self::CS;
}
|
省略的生命周期在其他地方可推断为 'static…
1
2
3
4
5
6
7
8
9
10
11
| #![allow(elided_lifetimes_in_associated_constant)]
# use core::marker::PhantomData;
# trait Single<'a>: 'a + Send + Sync {}
# struct L<'l, 'm>(&'l str, &'m str);
impl<'a, 'b> L<'a, 'b> {
const ECS: PhantomData<Box<dyn Single<'_>>> = PhantomData;
const SCS: PhantomData<Box<dyn Single<'static>>> = PhantomData;
const S_ECS: PhantomData<Box<dyn Single<'static> + 'static>> = Self::ECS;
const S_SCS: PhantomData<Box<dyn Single<'static> + 'static>> = Self::SCS;
}
|
然而, 它真的是自由变量。因此此类情况被视为歧义:
1
2
3
4
5
6
7
| #![allow(elided_lifetimes_in_associated_constant)]
# use core::marker::PhantomData;
# trait Double<'a, 'b>: 'a + 'b + Send + Sync {}
# struct L<'l, 'm>(&'l str, &'m str);
impl<'a, 'b> L<'a, 'b> {
const EBCD: PhantomData<Box<dyn Double<'a, '_>>> = PhantomData;
}
|
…此类情况被视为借用检查违反,因为匿名引入的生命周期参数之间没有 outlives 关系:
1
2
3
4
5
6
7
8
| #![allow(elided_lifetimes_in_associated_constant)]
# use core::marker::PhantomData;
# trait Single<'a>: 'a + Send + Sync {}
struct R<'l, 'm, 'r>(&'l str, &'m str, &'r ());
impl<'a, 'b, 'r> R<'a, 'b, 'r> where 'a: 'r, 'b: 'r {
const ECS: PhantomData<&dyn Single<'_>> = PhantomData;
const RECS: PhantomData<&'r dyn Single<'_>> = PhantomData;
}
|
(嵌套发生在 impl 块体而非头中,因此嵌套生命周期没有隐式约束。)
截至本小节最新更新,带 allow(elided_lifetimes_in_associated_constant) 的例子触发默认拒绝的前向兼容性 lint,可能最终完全禁止。
trait 约束在 impl 头中总是适用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| trait Single<'a>: 'a {}
trait Halfie<'a, 'b>: 'a {}
trait Double<'a, 'b>: 'a + 'b {}
struct S<T>(T);
// trait 约束适用
impl<'a> S<Box<dyn Single<'a>>> { fn f01() {} } // 'a(非 'static)
impl<'a, 'r> S<&'r dyn Single<'a>> { fn f02() {} } // 'a(非 'r)
impl<'a, 'b> S<Box<dyn Halfie<'a, 'b>>> { fn f03() {} } // 'a(非 'static)
impl<'a, 'b, 'r> S<&'r dyn Halfie<'a, 'b>> { fn f04() {} } // 'a(非 'r)
// 歧义(取消注释看错误)
// impl<'a, 'b> S<Box<dyn Double<'a, 'b>>> { fn f05() {} }
// impl<'a, 'b, 'r> S<&'r dyn Double<'a, 'b>> { fn f05() {} }
// 试 `+ 'static` 或 `+ 'r` 看错误
fn f<'a, 'b, 'r>(_: &'r &'a str, _: &'r &'b str) {
S::<Box<dyn Single<'a> + 'a>>::f01();
S::<&'r (dyn Single<'a> + 'a)>::f02();
S::<Box<dyn Halfie<'a, 'b> + 'a>>::f03();
S::<&'r (dyn Halfie<'a, 'b> + 'a)>::f04();
}
|
与函数签名一样,但与函数体不同,通配符生命周期 '_ 像普通省略一样(引入新的匿名生命周期变量)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| # trait Single<'a>: 'a {}
# trait Halfie<'a, 'b>: 'a {}
# trait Double<'a, 'b>: 'a + 'b {}
# struct S<T>(T);
// 通配符生命周期 `'_` 按正常引入独立生命周期
//(涵盖包括 `'static` 的所有情况)
impl<'a> S<Box<dyn Single<'a> + '_>> { fn f26() {} }
impl<'a, 'r> S<&'r (dyn Single<'a> + '_)> { fn f27() {} }
impl<'a, 'b> S<Box<dyn Halfie<'a, 'b> + '_>> { fn f28() {} }
impl<'a, 'b, 'r> S<&'r (dyn Halfie<'a, 'b> + '_)> { fn f29() {} }
impl<'a, 'b> S<Box<dyn Double<'a, 'b> + '_>> { fn f30() {} }
impl<'a, 'b, 'r> S<&'r (dyn Double<'a, 'b> + '_)> { fn f31() {} }
fn f<'a, 'b, 'r>(_: &'r &'a str, _: &'r &'b str) {
S::<Box<dyn Single<'a> + 'static>>::f26();
S::<&'r (dyn Single<'a> + 'static)>::f27();
S::<Box<dyn Halfie<'a, 'b> + 'static>>::f28();
S::<&'r (dyn Halfie<'a, 'b> + 'static)>::f29();
S::<Box<dyn Double<'a, 'b> + 'static>>::f30();
S::<&'r (dyn Double<'a, 'b> + 'static)>::f31();
}
|
关联类型
与 impl 头类似,trait 约束总是适用于关联类型。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| # use core::marker::PhantomData;
trait Single<'a>: 'a {}
trait Halfie<'a, 'b>: 'a {}
trait Double<'a, 'b>: 'a + 'b {}
trait Assoc {
type A01: ?Sized + Default;
type A02: ?Sized + Default;
//type A03: ?Sized + Default;
type A04: ?Sized + Default;
type A05: ?Sized + Default;
//type A06: ?Sized + Default;
}
impl<'r, 'a, 'b> Assoc for (&'r &'a (), &'r &'b ()) {
// 此处不允许 `'_`
// 此处不允许省略 `&`
type A01 = PhantomData<Box<dyn Single<'a>>>;
type A02 = PhantomData<Box<dyn Halfie<'a, 'b>>>;
// 歧义
// type A03 = PhantomData<Box<dyn Double<'a, 'b>>>;
type A04 = PhantomData<&'r dyn Single<'a>>;
type A05 = PhantomData<&'r dyn Halfie<'a, 'b>>;
// 歧义
// type A06 = PhantomData<&'r dyn Double<'a, 'b>>;
}
fn f<'r, 'a: 'r, 'b: 'r>() {
// 'a(非 `'static`、`'r`、`'b`)
let _: PhantomData<Box<dyn Single<'a> + 'a>> = <(&'r &'a (), &'r &'b ()) as Assoc>::A01::default();
let _: PhantomData<Box<dyn Halfie<'a, 'b> + 'a>> = <(&'r &'a (), &'r &'b ()) as Assoc>::A02::default();
let _: PhantomData<&'r (dyn Single<'a> + 'a)> = <(&'r &'a (), &'r &'b ()) as Assoc>::A04::default();
let _: PhantomData<&'r (dyn Halfie<'a, 'b> + 'a)> = <(&'r &'a (), &'r &'b ()) as Assoc>::A05::default();
}
|
注:未对与有界 trait 组合的 GAT 或本身有生命周期约束的关联类型进行广泛测试。