我有这个特点和简单的结构:
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>,
}
Run Code Online (Sandbox Code Playgroud)
我想实现以下Foo
特征Bar
:
impl Foo for Bar {
type Item = PathBuf;
type Iter = std::slice::Iter<PathBuf>;
fn get(&self) -> Self::Iter {
self.v.iter()
}
}
Run Code Online (Sandbox Code Playgroud)
但是我收到了这个错误:
error[E0106]: missing lifetime specifier
--> src/main.rs:16:17
|
16 | type Iter = std::slice::Iter<PathBuf>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter
Run Code Online (Sandbox Code Playgroud)
我发现无法在相关类型中指定生命周期.特别是我想表达迭代器不能超过self
生命周期.
我如何修改Foo
特征或Bar
特征实现来使其工作?
oli*_*obk 29
您的问题有两种解决方案.让我们从最简单的一个开始:
trait Foo<'a> {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&'a self) -> Self::Iter;
}
Run Code Online (Sandbox Code Playgroud)
这要求您在使用特征的任何地方注释生命周期.实现特征时,需要执行通用实现:
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()
}
}
Run Code Online (Sandbox Code Playgroud)
当您需要特征参数的特征时,还需要确保对特征对象的任何引用具有相同的生命周期:
fn fooget<'a, T: Foo<'a>>(foo: &'a T) {}
Run Code Online (Sandbox Code Playgroud)
不是为您的类型实现特征,而是实现它以引用您的类型.这种特性从来不需要了解有关生命的任何事情.
然后,特征函数必须按值获取其参数.在您的情况下,您将实现特征作为参考:
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()
}
}
Run Code Online (Sandbox Code Playgroud)
你的fooget
功能现在变得简单了
fn fooget<T: Foo>(foo: T) {}
Run Code Online (Sandbox Code Playgroud)
这个问题是该fooget
功能不知道T
实际上是一个&Bar
.当您调用该get
函数时,您实际上正在移出该foo
变量.您不会移出对象,只需移动参考.如果您的fooget
函数尝试调用get
两次,则该函数将无法编译.
如果希望fooget
函数只接受Foo
为引用实现特征的参数,则需要显式声明此绑定:
fn fooget_twice<'a, T>(foo: &'a T)
where
&'a T: Foo,
{}
Run Code Online (Sandbox Code Playgroud)
该where
子句确保您只Foo
为引用而不是类型的引用调用此函数.它也可以为两者实施.
从技术上讲,编译器可以自动推断生命周期,fooget_twice
因此您可以将其写为
n fooget_twice<T>(foo: &T)
where
&T: Foo,
{}
Run Code Online (Sandbox Code Playgroud)
但它不够聪明还.
对于更复杂的情况,您可以使用尚未实现的Rust功能:通用关联类型(GAT).问题44265正在跟踪这方面的工作.
如果特征及其所有实现都定义在一个包中,则辅助类型可能会很有用:
trait Foo {
fn get<'a>(&'a self) -> IterableFoo<'a, Self> {
IterableFoo(self)
}
}
struct IterableFoo<'a, T: ?Sized + Foo>(pub &'a T);
Run Code Online (Sandbox Code Playgroud)
对于实现 的具体类型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()
}
}
Run Code Online (Sandbox Code Playgroud)
该解决方案不允许在不同的板条箱中实现。另一个缺点是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());
}
}
Run Code Online (Sandbox Code Playgroud)
该特征可以定义一个关联类型,该类型提供对对象的一部分的访问,该对象绑定在引用中,提供必要的访问特征。
trait Foo {
type Iterable: ?Sized;
fn get(&self) -> &Self::Iterable;
}
Run Code Online (Sandbox Code Playgroud)
这要求任何实现类型都包含可以如此公开的部分:
impl Foo for Bar {
type Iterable = [PathBuf];
fn get(&self) -> &Self::Iterable {
&self.v
}
}
Run Code Online (Sandbox Code Playgroud)
在使用以下结果的泛型代码中对关联类型的引用进行限制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());
}
}
Run Code Online (Sandbox Code Playgroud)
该解决方案允许在特征定义箱之外实现。通用站点上的绑定工作与之前的解决方案一样令人烦恼。实现类型可能需要一个内部 shell 结构,其唯一目的是提供关联类型,以防使用站点边界不像讨论的示例中那样容易满足Vec
和。IntoIterator
归档时间: |
|
查看次数: |
5210 次 |
最近记录: |