简体   繁体   English

如何在关联类型中指定生命周期参数?

[英]How do I specify lifetime parameters in an associated type?

I have this trait and simple structure:我有这个特点和简单的结构:

use std::path::{Path, PathBuf};

trait Foo {
    type Item: AsRef<Path>;
    type Iter: Iterator<Item = Self::Item>;

    fn get(&self) -> Self::Iter;
}

struct Bar {
    v: Vec<PathBuf>,
}

I would like to implement the Foo trait for Bar :我想为Bar实现Foo特性:

impl Foo for Bar {
    type Item = PathBuf;
    type Iter = std::slice::Iter<PathBuf>;

    fn get(&self) -> Self::Iter {
        self.v.iter()
    }
}

However I'm getting this error:但是我收到此错误:

error[E0106]: missing lifetime specifier
  --> src/main.rs:16:17
   |
16 |     type Iter = std::slice::Iter<PathBuf>;
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter

I found no way to specify lifetimes inside that associated type.我发现无法在该关联类型中指定生命周期。 In particular I want to express that the iterator cannot outlive the self lifetime.我特别想表达的是迭代器的生命周期不能超过self生命周期。

How do I have to modify the Foo trait, or the Bar trait implementation, to make this work?我必须如何修改Foo特征或Bar特征实现才能使其工作?

Rust playground 铁锈游乐场

There are a two solutions to your problem.您的问题有两种解决方案。 Let's start with the simplest one:让我们从最简单的开始:

Add a lifetime to your trait为您的特质增添一生

trait Foo<'a> {
    type Item: AsRef<Path>;
    type Iter: Iterator<Item = Self::Item>;
    
    fn get(&'a self) -> Self::Iter;
}

This requires you to annotate the lifetime everywhere you use the trait.这要求您在使用该特征的任何地方注释生命周期。 When you implement the trait, you need to do a generic implementation:当你实现 trait 时,你需要做一个通用的实现:

impl<'a> Foo<'a> for Bar {
    type Item = &'a PathBuf;
    type Iter = std::slice::Iter<'a, PathBuf>;
    
    fn get(&'a self) -> Self::Iter {
        self.v.iter()
    }
}

When you require the trait for a generic argument, you also need to make sure that any references to your trait object have the same lifetime:当你需要一个泛型参数的 trait 时,你还需要确保对你的 trait 对象的任何引用都具有相同的生命周期:

fn fooget<'a, T: Foo<'a>>(foo: &'a T) {}

Implement the trait for a reference to your type实现 trait 以引用您的类型

Instead of implementing the trait for your type, implement it for a reference to your type.不要为您的类型实现 trait,而是为您的类型引用实现它。 The trait never needs to know anything about lifetimes this way. trait 永远不需要以这种方式了解有关生命周期的任何信息。

The trait function then must take its argument by value.然后特征函数必须按值获取其参数。 In your case you will implement the trait for a reference:在您的情况下,您将实现 trait 以供参考:

trait Foo {
    type Item: AsRef<Path>;
    type Iter: Iterator<Item = Self::Item>;
    
    fn get(self) -> Self::Iter;
}

impl<'a> Foo for &'a Bar {
    type Item = &'a PathBuf;
    type Iter = std::slice::Iter<'a, PathBuf>;
    
    fn get(self) -> Self::Iter {
        self.v.iter()
    }
}

Your fooget function now simply becomes你的fooget函数现在简单地变成

fn fooget<T: Foo>(foo: T) {}

The problem with this is that the fooget function doesn't know T is in reality a &Bar .问题在于fooget函数不知道T实际上是&Bar When you call the get function, you are actually moving out of the foo variable.当您调用get函数时,您实际上是在移出foo变量。 You don't move out of the object, you just move the reference.您不会移出对象,而只是移动参考。 If your fooget function tries to call get twice, the function won't compile.如果您的fooget函数尝试调用get两次,则该函数将无法编译。

If you want your fooget function to only accept arguments where the Foo trait is implemented for references, you need to explicitly state this bound:如果您希望fooget函数仅接受为引用实现了Foo特征的参数,则需要明确声明此界限:

fn fooget_twice<'a, T>(foo: &'a T)
where
    &'a T: Foo,
{}

The where clause makes sure that you only call this function for references where Foo was implemented for the reference instead of the type. where子句确保您只为引用调用此函数,其中为引用而不是类型实现了Foo It may also be implemented for both.也可以对两者实施。

Technically, the compiler could automatically infer the lifetime in fooget_twice so you could write it as从技术上讲,编译器可以自动推断fooget_twice的生命周期,因此您可以将其写为

fn fooget_twice<T>(foo: &T)
where
    &T: Foo,
{}

but it's not smart enough yet .但它不是足够聪明尚未


For more complicated cases, you can use a Rust feature which is not yet implemented: Generic Associated Types (GATs).对于更复杂的情况,您可以使用尚未实现的 Rust 功能: 通用关联类型(GAT)。 Work for that is being tracked in issue 44265 .相关工作在issue 44265 中进行跟踪。

Use a wrapper type使用包装类型

If the trait and all its implementations are defined in one crate, a helper type can be useful:如果 trait 及其所有实现都定义在一个 crate 中,则辅助类型可能很有用:

trait Foo {
    fn get<'a>(&'a self) -> IterableFoo<'a, Self> {
        IterableFoo(self)
    }
}

struct IterableFoo<'a, T: ?Sized + Foo>(pub &'a T);

For a concrete type that implements Foo , implement the iterator conversion on the IterableFoo wrapping it:对于实现Foo的具体类型,在包装它的IterableFoo上实现迭代器转换:

impl Foo for Bar {}

impl<'a> IntoIterator for IterableFoo<'a, Bar> {
    type Item = &'a PathBuf;
    type IntoIter = std::slice::Iter<'a, PathBuf>;
    fn into_iter(self) -> Self::IntoIter {
        self.0.v.iter()
    }
}

This solution does not allow implementations in a different crate.此解决方案不允许在不同的板条箱中实现。 Another disadvantage is that an IntoIterator bound cannot be encoded into the definition of the trait, so it will need to be specified as an additional (and higher-rank) bound for generic code that wants to iterate over the result of Foo::get :另一个缺点是IntoIterator绑定无法编码到特征的定义中,因此需要将其指定为想要迭代Foo::get结果的通用代码的附加(和更高等级)绑定:

fn use_foo_get<T>(foo: &T)
where
    T: Foo,
    for<'a> IterableFoo<'a, T>: IntoIterator,
    for<'a> <IterableFoo<'a, T> as IntoIterator>::Item: AsRef<Path>
{
    for p in foo.get() {
        println!("{}", p.as_ref().to_string_lossy());
    }
}

Associated type for an internal object providing desired functionality提供所需功能的内部对象的关联类型

The trait can define an associated type that gives access to a part of the object that, bound in a reference, provides the necessary access traits. trait 可以定义一个关联类型,该类型可以访问绑定在引用中的对象的一部分,提供必要的访问 trait。

trait Foo {
    type Iterable: ?Sized;

    fn get(&self) -> &Self::Iterable;
}

This requires that any implementation type contains a part that can be so exposed:这要求任何实现类型都包含可以如此公开的部分:

impl Foo for Bar {
    type Iterable = [PathBuf];

    fn get(&self) -> &Self::Iterable {
        &self.v
    }
}

Put bounds on the reference to the associated type in generic code that uses the the result of get :在使用get结果的泛型代码中对关联类型的引用设置边界:

fn use_foo_get<'a, T>(foo: &'a T)
where
    T: Foo,
    &'a T::Iterable: IntoIterator,
    <&'a T::Iterable as IntoIterator>::Item: AsRef<Path>
{
    for p in foo.get() {
        println!("{}", p.as_ref().to_string_lossy());
    }
}

This solution permits implementations outside of the trait definition crate.此解决方案允许在 trait 定义包之外的实现。 The bound work at generic use sites is as annoying as with the previous solution.通用网站上的绑定工作与之前的解决方案一样令人讨厌。 An implementing type may need an internal shell struct with the only purpose of providing the associated type, in case when the use-site bounds are not as readily satisfied as with Vec and IntoIterator in the example discussed.实现类型可能需要一个内部 shell 结构,其唯一目的是提供关联类型,以防使用站点边界不像讨论的示例中的VecIntoIterator那样容易满足。

In future, you'll want an associated type constructor for your lifetime 'a but Rust does not support that yet.将来,您将需要一个关联的类型构造函数,用于您的生命周期'a但 Rust 尚不支持。 See RFC 1598请参阅RFC 1598

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何使用生命周期参数指定关联类型? - How do I specify an associated type with a lifetime parameter? 如何指定引用自身但不会自我变异的迭代器的关联类型的生命周期? - How do I specify the lifetime for the associated type of an iterator that refers to itself but does not mutate itself? 如何为将成为闭包参数的关联类型指定生命周期? - How to specify lifetime for associated type that will be a closure argument? 如何为包含对具有生命周期参数的类型的引用的类型声明生命周期参数? - How do I declare the lifetime parameters for a type which contains a reference to a type with a lifetime parameter? 如何指定 AsRef 的生命周期? - How do I specify the lifetime of an AsRef? 使用泛型关联类型时,如何指示类型参数的生命周期? - How do I indicate a lifetime bound on a type parameter when using generic associated types? 如何限制关联类型接受任何引用生命周期? - How can I constrain an associated type to accept any reference lifetime? 如何指定依赖于单独类型的闭包借用绑定的生存期? - How do I specify a lifetime that is dependent on the borrowed binding of a closure in a separate type? 如何指定一个带引用的闭包,并返回实现与引用具有相同生命周期的特征的任何类型? - How do I specify a closure that takes a reference and returns any type implementing a trait with the same lifetime as the reference? 如何在具有生命周期限制的类型上命名关联类型? - How to name an associated type on a type with a lifetime bound?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM