当前位置: 首页 > 知识库问答 >
问题:

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

牛嘉谊
2023-03-14

我有这种类型:

struct Wrap<T>(Vec<T>);

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

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生命周期

现在我可以将绑定到参数T静态,但我认为这是过度约束的。在不使用关联类型时,我可以轻松实现这种方法:

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

不需要的静态绑定,因为现在签名deugars到:

fn my_index<'a>(&'a self, index: usize) -> &'a Display + 'a

这是有道理的:trait对象必须至少生存'a。(操场上有所有的代码)。

但是我可以使用关联类型(如Indextrait)来完成这项工作吗?我觉得这可能适用于泛型关联类型,但是(a)我不确定,(b)它们还没有实现。

共有3个答案

姜锋
2023-03-14

嗨,我遇到了和你一样的问题。“像

我没有发现生锈是否发布了一些相关功能。但是我想出了一个相当愚蠢的方法来满足我的要求。

方法仅在实现特性的结构是可枚举的时才起作用。假设您有三个结构Index1,Index2,Index3,它们都实现了traitIndex

然后我们可以简单地用

pub enum Indices{
    Index1(Index1),
    Index2(Index2),
    Index3(Index3),
}

然后实现枚举及其所有变体的特征,有一个例子:rust-如何实现枚举及其相应变体的特征堆栈溢出

华锦
2023-03-14

返回对T的引用,而不是返回对trait对象的引用,让用户强制转换到trait对象,或者让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]
    }
}
唐沈义
2023-03-14

一种尝试是将一生附加到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

错误代码E0207建议了几种解决方案,但由于我们无法更改索引特征,唯一可接受的解决方案是使包装捕获无约束的生存期参数:

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,额外的生命周期会感染任何地方...如果这是不可接受的,你也可以

  • 不使用Indextrait,而是引入自己的终身敏感trait(因此用户需要使用w.my_index(i)而不是

 类似资料:
  • 我正在Rust中编写一个带有控制台提示界面的进程内存扫描器。 我需要扫描仪类型,如winapi扫描仪或ring0驱动程序扫描仪,所以我试图实现多态性。 目前我有以下结构: 将来,除了 之外,我还会有更多扫描仪类型,因此,如果我理解正确,我应该使用特征引用 (

  • 我有一个特质,旨在加强对另一个特质的约束,例如: 如果我使用的是泛型而不是关联类型,我将能够告诉Rust < code > MyAssoc 在traits 和< code>B之间是相同的: 我如何对关联的类型做同样的事情?

  • 问题内容: 直到几周前,我还以为我知道何时创建字段和方法或。例如,当一个字段(例如另一个类的对象)对于该类的任意数量的对象是唯一的时,应将其设置为。 但是几周后,我读到了有关JVM垃圾收集的信息。 我知道字段永远不会被垃圾收集,并且始终保持在内存中,除非类加载器本身是垃圾收集的。 但是,如果我不创建该字段,至少它将被垃圾回收。 因此,在使字段/方法静态化与否之间似乎有一条 很细的界限 。 有人可以

  • 尝试可视化和理解同步。 对同步块使用静态锁定对象(代码a)和非静态锁定对象(代码B)之间有什么区别 它在实际应用中有什么不同 一方会有哪些陷阱而另一方不会 确定使用哪一个的标准是什么 代码A 代码B 笔记 上面的代码显示了构造函数,但是您可以讨论静态方法和非静态方法中的行为是如何不同的。另外,在同步块修改静态成员变量时使用静态锁是否有利? 我已经看过了这个问题的答案,但是还不清楚不同的使用场景是什

  • 我在看一个项目,我发现了一些很奇怪的东西。 现在,我认为唯一的两个优点是,在非静态容器中封装时,命名更加清晰,并且可以传递的参数更少。 但我想知道这是不是一个好主意,通过设计来包装静态类与非静态?如果有的话,还有哪些其他原因?因为我认为创建一个静态并对其进行调用是可以的。但是这个项目特意将所有静态类打包;我也不知道为什么。

  • 问题内容: 这些对象调用之间有什么区别? 非静态: 静态的: 而且在内部为什么还要对函数使用static属性? 例: 问题答案: 静态函数,根据定义,不能也不依赖于该类的任何实例属性。也就是说,它们不需要类的实例来执行(因此,可以如您所显示的那样执行,而无需先创建实例)。从某种意义上讲,这意味着该函数不必(也永远不需要)依赖于类的成员或方法(公共或私有)。