如何使用非关联类型的非静态特征对象?

Luk*_*odt 4 traits rust associated-types trait-objects

我有这种类型:

struct Wrap<T>(Vec<T>);
Run Code Online (Sandbox Code Playgroud)

我想实现std::ops::Index并返回对特征对象的引用.这是我的第一次尝试(游乐场):

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]
    }
}
Run Code Online (Sandbox Code Playgroud)

这不起作用并导致此错误:

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]
   |         ^^^^^^^^^^^^^^
Run Code Online (Sandbox Code Playgroud)

我想我知道为什么会发生这种情况:type Output = Display等同于type Output = Display + 'static每个特征对象都带有默认的生命周期限制'static.

所以现在我可以将'static绑定添加到我的参数中T,但我认为这是过度约束的.不使用关联类型时,我可以轻松实现这样的方法:

impl<T> Wrap<T>
where
    T: Display,
{
    fn my_index(&self, index: usize) -> &Display {
        &self.0[index]
    }
}
Run Code Online (Sandbox Code Playgroud)

没有'static必要,因为现在签名desugars:

fn my_index<'a>(&'a self, index: usize) -> &'a Display + 'a
Run Code Online (Sandbox Code Playgroud)

这是有道理的:特质对象必须至少生存'a.(带有所有代码的游乐场).


但是我可以使用相关类型(比如Index特征)来完成这项工作吗?我觉得这可能适用于通用关联类型,但(a)我不确定和(b)它们尚未实现.

ken*_*ytm 5

一种尝试是将生命附加到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]
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,编译器不会接受它,因为'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
Run Code Online (Sandbox Code Playgroud)

错误代码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"
}
Run Code Online (Sandbox Code Playgroud)

(游乐场)

当然,这会改变你的API,并且额外的生命周期会在任何地方感染......如果这是不可接受的,你也可以

  • 不使用Index特性,而是引入自己的生命敏感特性(因此用户需要使用w.my_index(i)而不是&w[i]); 要么
  • 设置Output = Display + 'static并排除所有瞬态类型.用户需要克隆或使用Rc.