如何在关联类型中指定生命周期参数?
How do I specify lifetime parameters in an associated type?
我有这个特点,结构简单:
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>,
}
我想为 Bar
实现 Foo
特性:
impl Foo for Bar {
type Item = PathBuf;
type Iter = std::slice::Iter<PathBuf>;
fn get(&self) -> Self::Iter {
self.v.iter()
}
}
但是我收到这个错误:
error[E0106]: missing lifetime specifier
--> src/main.rs:16:17
|
16 | type Iter = std::slice::Iter<PathBuf>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter
我发现无法在关联类型中指定生命周期。特别是我想表达迭代器不能超过 self
生命周期。
我如何修改 Foo
特征或 Bar
特征实现,才能使其工作?
您的问题有两种解决方案。先从最简单的说起:
为你的特质添加一生
trait Foo<'a> {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&'a self) -> Self::Iter;
}
这要求您在使用特征的任何地方注释生命周期。当你实现 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()
}
}
当您需要通用参数的特征时,您还需要确保对特征对象的任何引用都具有相同的生命周期:
fn fooget<'a, T: Foo<'a>>(foo: &'a T) {}
实现对你的类型的引用的特性
不是为你的类型实现特征,而是为你的类型的引用实现它。这种特征永远不需要知道任何关于生命周期的信息。
然后 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()
}
}
您的 fooget
函数现在变成了
fn fooget<T: Foo>(foo: T) {}
问题在于 fooget
函数不知道 T
实际上是 &Bar
。当你调用get
函数时,实际上是在移出foo
变量。您不会移出对象,只需移动参考即可。如果您的 fooget
函数尝试调用 get
两次,该函数将无法编译。
如果您希望 fooget
函数只接受为引用实现 Foo
特征的参数,您需要明确声明此绑定:
fn fooget_twice<'a, T>(foo: &'a T)
where
&'a T: Foo,
{}
where
子句确保您只为引用调用此函数,其中 Foo
是为引用而不是类型实现的。它也可以同时实现。
从技术上讲,编译器可以自动推断 fooget_twice
中的生命周期,因此您可以将其写为
fn fooget_twice<T>(foo: &T)
where
&T: Foo,
{}
但它不够聪明yet。
对于更复杂的情况,您可以使用尚未实现的 Rust 功能:Generic Associated Types (GATs). Work for that is being tracked in issue 44265。
以后,你会want an associated type constructor for your lifetime 'a
but Rust does not support that yet. See RFC 1598
使用包装类型
如果 trait 及其所有实现都定义在一个 crate 中,则辅助类型可能很有用:
trait Foo {
fn get<'a>(&'a self) -> IterableFoo<'a, Self> {
IterableFoo(self)
}
}
struct IterableFoo<'a, T: ?Sized + Foo>(pub &'a T);
对于实现 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()
}
}
此解决方案不允许在不同的 crate 中实现。另一个缺点是 IntoIterator
绑定无法编码到特征的定义中,因此需要将其指定为附加(和更高等级)绑定,用于想要迭代 [ 的结果的通用代码=19=]:
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());
}
}
提供所需功能的内部对象的关联类型
特征可以定义一个关联类型,该类型提供对对象的一部分的访问,该部分绑定在引用中,提供必要的访问特征。
trait Foo {
type Iterable: ?Sized;
fn get(&self) -> &Self::Iterable;
}
这要求任何实现类型都包含可以如此公开的部分:
impl Foo for Bar {
type Iterable = [PathBuf];
fn get(&self) -> &Self::Iterable {
&self.v
}
}
在使用 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());
}
}
此解决方案允许在特征定义箱之外实施。
通用使用站点的绑定工作与以前的解决方案一样烦人。
一个实现类型可能需要一个内部 shell 结构,其唯一目的是提供关联类型,以防使用站点边界不像 Vec
和 IntoIterator
那样容易满足讨论的例子。
我有这个特点,结构简单:
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>,
}
我想为 Bar
实现 Foo
特性:
impl Foo for Bar {
type Item = PathBuf;
type Iter = std::slice::Iter<PathBuf>;
fn get(&self) -> Self::Iter {
self.v.iter()
}
}
但是我收到这个错误:
error[E0106]: missing lifetime specifier
--> src/main.rs:16:17
|
16 | type Iter = std::slice::Iter<PathBuf>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter
我发现无法在关联类型中指定生命周期。特别是我想表达迭代器不能超过 self
生命周期。
我如何修改 Foo
特征或 Bar
特征实现,才能使其工作?
您的问题有两种解决方案。先从最简单的说起:
为你的特质添加一生
trait Foo<'a> {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&'a self) -> Self::Iter;
}
这要求您在使用特征的任何地方注释生命周期。当你实现 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()
}
}
当您需要通用参数的特征时,您还需要确保对特征对象的任何引用都具有相同的生命周期:
fn fooget<'a, T: Foo<'a>>(foo: &'a T) {}
实现对你的类型的引用的特性
不是为你的类型实现特征,而是为你的类型的引用实现它。这种特征永远不需要知道任何关于生命周期的信息。
然后 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()
}
}
您的 fooget
函数现在变成了
fn fooget<T: Foo>(foo: T) {}
问题在于 fooget
函数不知道 T
实际上是 &Bar
。当你调用get
函数时,实际上是在移出foo
变量。您不会移出对象,只需移动参考即可。如果您的 fooget
函数尝试调用 get
两次,该函数将无法编译。
如果您希望 fooget
函数只接受为引用实现 Foo
特征的参数,您需要明确声明此绑定:
fn fooget_twice<'a, T>(foo: &'a T)
where
&'a T: Foo,
{}
where
子句确保您只为引用调用此函数,其中 Foo
是为引用而不是类型实现的。它也可以同时实现。
从技术上讲,编译器可以自动推断 fooget_twice
中的生命周期,因此您可以将其写为
fn fooget_twice<T>(foo: &T)
where
&T: Foo,
{}
但它不够聪明yet。
对于更复杂的情况,您可以使用尚未实现的 Rust 功能:Generic Associated Types (GATs). Work for that is being tracked in issue 44265。
以后,你会want an associated type constructor for your lifetime 'a
but Rust does not support that yet. See RFC 1598
使用包装类型
如果 trait 及其所有实现都定义在一个 crate 中,则辅助类型可能很有用:
trait Foo {
fn get<'a>(&'a self) -> IterableFoo<'a, Self> {
IterableFoo(self)
}
}
struct IterableFoo<'a, T: ?Sized + Foo>(pub &'a T);
对于实现 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()
}
}
此解决方案不允许在不同的 crate 中实现。另一个缺点是 IntoIterator
绑定无法编码到特征的定义中,因此需要将其指定为附加(和更高等级)绑定,用于想要迭代 [ 的结果的通用代码=19=]:
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());
}
}
提供所需功能的内部对象的关联类型
特征可以定义一个关联类型,该类型提供对对象的一部分的访问,该部分绑定在引用中,提供必要的访问特征。
trait Foo {
type Iterable: ?Sized;
fn get(&self) -> &Self::Iterable;
}
这要求任何实现类型都包含可以如此公开的部分:
impl Foo for Bar {
type Iterable = [PathBuf];
fn get(&self) -> &Self::Iterable {
&self.v
}
}
在使用 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());
}
}
此解决方案允许在特征定义箱之外实施。
通用使用站点的绑定工作与以前的解决方案一样烦人。
一个实现类型可能需要一个内部 shell 结构,其唯一目的是提供关联类型,以防使用站点边界不像 Vec
和 IntoIterator
那样容易满足讨论的例子。