使用包装类型
如果特质和所有实现都在同一个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
限定无法编码进trait的定义中,因此需要将其作为附加(和更高级别的)限定来指定泛型代码,以便迭代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());
}
}
为提供所需功能的内部对象定义关联类型
该trait可以定义一个关联类型,通过绑定在引用中的对象的一部分,提供所需的访问trait。
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());
}
}
这个解决方案允许在特质定义的 crate 之外实现。
与先前的解决方案一样,泛型使用点上的边界工作令人烦恼。
如果使用点的边界不像在例子中讨论的 Vec
和 IntoIterator
那样容易满足,那么实现类型可能需要一个内部壳结构体,其唯一目的是提供关联类型。
fooget
类似函数的角度来看,它似乎会带来更少的负担。相对于第二种解决方案,这个特征也更加明确。 - mbrtT :: Item
吗?在这种情况下,T
没有实现Foo
,只有&'a T:Foo
。编辑:我找到了!它是<&T as Foo> :: Item
。 - 张实唯