如何使用关联类型的非静态特征对象?
How to use non-'static trait objects with associated types?
我有这种类型:
struct Wrap<T>(Vec<T>);
我想实现 std::ops::Index
和 return 对特征对象的引用。这是我的第一次尝试 (Playground):
use std::ops::Index;
use std::fmt::Display;
impl<T> Index<usize> for Wrap<T>
where
T: Display
{
type Output = Display;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
这不起作用并导致此错误:
error[E0310]: the parameter type `T` may not live long enough
--> src/main.rs:13:9
|
7 | impl<T> Index<usize> for Wrap<T>
| - help: consider adding an explicit lifetime bound `T: 'static`...
...
13 | &self.0[index]
| ^^^^^^^^^^^^^^
|
note: ...so that the type `T` will meet its required lifetime bounds
--> src/main.rs:13:9
|
13 | &self.0[index]
| ^^^^^^^^^^^^^^
我想我知道为什么会发生这种情况:type Output = Display
等同于 type Output = Display + 'static
,因为每个 trait 对象都有生命周期,默认为 'static
。
所以现在我可以将 'static
绑定添加到我的参数 T
,但我认为这是过度约束。我可以在不使用关联类型时轻松实现这样的方法:
impl<T> Wrap<T>
where
T: Display,
{
fn my_index(&self, index: usize) -> &Display {
&self.0[index]
}
}
不需要 'static
绑定,因为现在签名脱糖为:
fn my_index<'a>(&'a self, index: usize) -> &'a Display + 'a
这是有道理的:特征对象必须至少存活 'a
。 (Playground with all the code).
但是我可以使用关联类型(比如 Index
特征)来完成这项工作吗?我感觉这可能适用于通用关联类型,但是 (a) 我不确定并且 (b) 它们尚未实现。
一种尝试是将生命周期附加到 impl:
// Note: won't work.
impl<'a, T> Index<usize> for Wrap<T>
where
T: Display + 'a,
{
type Output = Display + 'a;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
但是,编译器不会接受它,因为没有使用'a
。
error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
--> src/main.rs:7:6
|
7 | impl<'a, T> Index<usize> for Wrap<T>
| ^^ unconstrained lifetime parameter
error code E0207 提出了几种解决方案,但由于我们无法更改 Index
特征,唯一可接受的解决方案是让 Wrap
捕获不受约束的生命周期参数:
use std::ops::Index;
use std::fmt::Display;
use std::marker::PhantomData;
struct Wrap<'a, T>(Vec<T>, PhantomData<&'a ()>);
// ^~ ^~~~~~~~~~~~~~~~~~~
impl<'a, T> Index<usize> for Wrap<'a, T>
where
T: Display + 'a,
{
type Output = Display + 'a;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
fn main() {
let w = Wrap(vec!['a', 'b'], PhantomData);
println!("{}", &w[0]); // prints "a"
let s = "hi".to_string();
let w = Wrap(vec![&s], PhantomData);
println!("{}", &w[0]); // prints "hi"
}
当然,这会改变你的 API 并且额外的生命会感染所有地方......如果这是不可接受的,你可以
- 不使用
Index
trait,而是引入你自己的 lifetime-sensitive trait(因此用户需要使用 w.my_index(i)
而不是 &w[i]
);或者
- 设置
Output = Display + 'static
,并排除所有瞬态类型。用户将需要克隆或使用 Rc
.
Return 对 T
的引用而不是返回对特征对象的引用并让用户转换为特征对象,或者让 Rust 从上下文隐式推断何时执行演员:
use std::fmt::Display;
use std::ops::Index;
fn main() {
let w1 = Wrap(vec!['I', 'b']);
let s = "am".to_string();
let w2 = Wrap(vec![&s]);
let w3 = Wrap(vec![1, 2]);
let mut trait_store: Vec<Box<Display>> = Vec::new();
trait_store.push(Box::new(w1.index(0)));
trait_store.push(Box::new(w2.index(0)));
trait_store.push(Box::new(w3.index(0)));
for el in trait_store {
println!("{}", el);
}
}
struct Wrap<T>(Vec<T>);
impl<T> Index<usize> for Wrap<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
你好,我遇到了和你一样的问题。 “像 &Index<usize, Output = Display>
。这不能直接与 Index 一起使用,但在我的问题中使用 Index 使它更容易一些。”
我没有查到Rust是否发布了一些相关的特性。但是我想出了一个相当愚蠢的方法来满足我的要求。
此方法仅在实现特征的结构可枚举时有效。假设你有三个结构 Index1, Index2, Index3
,它们都实现了特征 Index<usize, Output = Display>
然后我们可以通过
简单地包装这些结构
pub enum Indices{
Index1(Index1),
Index2(Index2),
Index3(Index3),
}
然后为枚举及其所有变体实现特征,有一个例子:
我有这种类型:
struct Wrap<T>(Vec<T>);
我想实现 std::ops::Index
和 return 对特征对象的引用。这是我的第一次尝试 (Playground):
use std::ops::Index;
use std::fmt::Display;
impl<T> Index<usize> for Wrap<T>
where
T: Display
{
type Output = Display;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
这不起作用并导致此错误:
error[E0310]: the parameter type `T` may not live long enough
--> src/main.rs:13:9
|
7 | impl<T> Index<usize> for Wrap<T>
| - help: consider adding an explicit lifetime bound `T: 'static`...
...
13 | &self.0[index]
| ^^^^^^^^^^^^^^
|
note: ...so that the type `T` will meet its required lifetime bounds
--> src/main.rs:13:9
|
13 | &self.0[index]
| ^^^^^^^^^^^^^^
我想我知道为什么会发生这种情况:type Output = Display
等同于 type Output = Display + 'static
,因为每个 trait 对象都有生命周期,默认为 'static
。
所以现在我可以将 'static
绑定添加到我的参数 T
,但我认为这是过度约束。我可以在不使用关联类型时轻松实现这样的方法:
impl<T> Wrap<T>
where
T: Display,
{
fn my_index(&self, index: usize) -> &Display {
&self.0[index]
}
}
不需要 'static
绑定,因为现在签名脱糖为:
fn my_index<'a>(&'a self, index: usize) -> &'a Display + 'a
这是有道理的:特征对象必须至少存活 'a
。 (Playground with all the code).
但是我可以使用关联类型(比如 Index
特征)来完成这项工作吗?我感觉这可能适用于通用关联类型,但是 (a) 我不确定并且 (b) 它们尚未实现。
一种尝试是将生命周期附加到 impl:
// Note: won't work.
impl<'a, T> Index<usize> for Wrap<T>
where
T: Display + 'a,
{
type Output = Display + 'a;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
但是,编译器不会接受它,因为没有使用'a
。
error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
--> src/main.rs:7:6
|
7 | impl<'a, T> Index<usize> for Wrap<T>
| ^^ unconstrained lifetime parameter
error code E0207 提出了几种解决方案,但由于我们无法更改 Index
特征,唯一可接受的解决方案是让 Wrap
捕获不受约束的生命周期参数:
use std::ops::Index;
use std::fmt::Display;
use std::marker::PhantomData;
struct Wrap<'a, T>(Vec<T>, PhantomData<&'a ()>);
// ^~ ^~~~~~~~~~~~~~~~~~~
impl<'a, T> Index<usize> for Wrap<'a, T>
where
T: Display + 'a,
{
type Output = Display + 'a;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
fn main() {
let w = Wrap(vec!['a', 'b'], PhantomData);
println!("{}", &w[0]); // prints "a"
let s = "hi".to_string();
let w = Wrap(vec![&s], PhantomData);
println!("{}", &w[0]); // prints "hi"
}
当然,这会改变你的 API 并且额外的生命会感染所有地方......如果这是不可接受的,你可以
- 不使用
Index
trait,而是引入你自己的 lifetime-sensitive trait(因此用户需要使用w.my_index(i)
而不是&w[i]
);或者 - 设置
Output = Display + 'static
,并排除所有瞬态类型。用户将需要克隆或使用Rc
.
Return 对 T
的引用而不是返回对特征对象的引用并让用户转换为特征对象,或者让 Rust 从上下文隐式推断何时执行演员:
use std::fmt::Display;
use std::ops::Index;
fn main() {
let w1 = Wrap(vec!['I', 'b']);
let s = "am".to_string();
let w2 = Wrap(vec![&s]);
let w3 = Wrap(vec![1, 2]);
let mut trait_store: Vec<Box<Display>> = Vec::new();
trait_store.push(Box::new(w1.index(0)));
trait_store.push(Box::new(w2.index(0)));
trait_store.push(Box::new(w3.index(0)));
for el in trait_store {
println!("{}", el);
}
}
struct Wrap<T>(Vec<T>);
impl<T> Index<usize> for Wrap<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
你好,我遇到了和你一样的问题。 “像 &Index<usize, Output = Display>
。这不能直接与 Index 一起使用,但在我的问题中使用 Index 使它更容易一些。”
我没有查到Rust是否发布了一些相关的特性。但是我想出了一个相当愚蠢的方法来满足我的要求。
此方法仅在实现特征的结构可枚举时有效。假设你有三个结构 Index1, Index2, Index3
,它们都实现了特征 Index<usize, Output = Display>
然后我们可以通过
简单地包装这些结构pub enum Indices{
Index1(Index1),
Index2(Index2),
Index3(Index3),
}
然后为枚举及其所有变体实现特征,有一个例子: