提问人:ynn 提问时间:6/17/2023 最后编辑:ynn 更新时间:11/17/2023 访问量:104
如何从trait方法返回std::iter::Map?
How to return std::iter::Map from trait method?
问:
我有一个元组向量:
let l = vec![(0, 1), (2, 3)];
我想得到哪个提取每个元组的第一个元素。天真地,这是有效的:std::iter::Map
let m: std::iter::Map<_, _> = l.iter().map(|e| e.0);
现在我想扩展一下,使这个伪代码工作:Iterator
let m: std::iter::Map<_, _> = l.iter().tuple_first();
尝试 1
我试图返回:impl Iterator
trait TupleExtractor<'a, T1: 'a, T2: 'a>: Iterator<Item = &'a (T1, T2)> {
fn tuple_first(self) -> impl Iterator<Item = &'a T1>;
}
impl<'a, T1: 'a + Copy, T2: 'a, I> TupleExtractor<'a, T1, T2> for I
where
I: Iterator<Item = &'a (T1, T2)>,
{
fn tuple_first(self) -> impl Iterator<Item = &'a T1> {
self.map(|e| e.0)
}
}
此代码无法编译,因为
`impl Trait` only allowed in function and inherent method return types, not in trait method return types
尝试 2
我试图显式返回而不是.但是,出于同样的原因,此代码也不会编译。std::iter::Map
impl Iterator
trait TupleExtractor<'a, T1: 'a, T2: 'a>: Iterator<Item = &'a (T1, T2)> {
fn tuple_first(
self,
) -> std::iter::Map<impl Iterator<Item = &'a (T1, T2)>, impl FnMut(&'a (T1, T2)) -> T1>;
}
impl<'a, T1: 'a + Copy, T2: 'a, I> TupleExtractor<'a, T1, T2> for I
where
I: Iterator<Item = &'a (T1, T2)>,
{
fn tuple_first(self) -> std::iter::Map<I, impl FnMut(&'a (T1, T2)) -> T1> {
self.map(|e| e.0)
}
}
尝试 3
由于尝试 2 不起作用的原因是我无法指定 的类型参数,我尝试将它们设置为类型参数:std::iter::Map
trait TupleExtractor<'a, T1: 'a, T2: 'a, A, B>: Iterator<Item = &'a (T1, T2)> {
fn tuple_first(self) -> std::iter::Map<A, B>;
}
impl<'a, T1: 'a + Copy, T2: 'a, I, F> TupleExtractor<'a, T1, T2, I, F> for I
where
I: Iterator<Item = &'a (T1, T2)>,
F: FnMut(&'a (T1, T2)) -> T1,
{
fn tuple_first(self) -> std::iter::Map<I, F> {
self.map(|e: &'a (T1, T2)| e.0)
}
}
此代码不编译,因为闭包的类型不是:|e: &'a (T1, T2)| e.0
F
= note: expected type parameter `F`
found closure `[closure@src/main.rs:38:18: 38:35]`
= help: every closure has a distinct type and so could not always match the caller-chosen type of parameter `F`
如何从特质方法返回?我不想使用 .std::iter::Map
Box
答:
我能想到的解决这个问题的唯一方法是禁止使用捕获闭包(因为它们的类型还不能在特征中命名)以支持函数指针。然后,您可以使用关联类型定义您的特征,而不是返回位置 impl 特征。这显然限制了可能的实现,但它仍然允许您展示的用例:
trait TupleExtractor<'a, T1: 'a, T2: 'a>: Iterator<Item = &'a (T1, T2)> {
type Mapper: Iterator<Item = T1>;
fn tuple_first(
self,
) -> Self::Mapper;
// ^^^^^^^^^^^^ replace return position impl trait with associated type
}
impl<'a, T1: 'a + Copy, T2: 'a, I> TupleExtractor<'a, T1, T2> for I
where
I: Iterator<Item = &'a (T1, T2)>,
{
type Mapper = std::iter::Map<I, fn(&'a (T1, T2)) -> T1>;
// ^^^^^^^^^^^^^^^^^^^^^^ we can actually name this type
fn tuple_first(self) -> Self::Mapper {
// implementation stays the same because this is just an anonymous function
// not a closure
self.map(|e| e.0)
}
}
#[test]
fn test_tuple_first() {
let v = vec![(1, 'a'), (2, 'b'), (3, 'b')];
assert!(v.iter().tuple_first().eq([1, 2, 3].into_iter()))
}
如果该类型需要针对其他潜在的特征方法更具可扩展性,则可以将其设置为泛型,而不是它生成的项类型:Mapper
trait TupleExtractor<'a, T1: 'a, T2: 'a>: Iterator<Item = &'a (T1, T2)> {
type Mapper<T>: Iterator<Item = T>
where
T: 'a;
fn tuple_first(
self,
) -> Self::Mapper<T1>;
fn tuple_second(
self,
) -> Self::Mapper<T2>;
}
impl<'a, T1: 'a + Copy, T2: 'a + Copy, I> TupleExtractor<'a, T1, T2> for I
where
I: Iterator<Item = &'a (T1, T2)>,
{
type Mapper<T> = std::iter::Map<I, fn(&'a (T1, T2)) -> T> where T: 'a;
fn tuple_first(self) -> Self::Mapper<T1> {
self.map(|e| e.0)
}
fn tuple_second(self) -> Self::Mapper<T2> {
self.map(|e| e.1)
}
}
#[test]
fn test_tuple_first() {
let v = vec![(1, 'a'), (2, 'b'), (3, 'b')];
assert!(v.iter().tuple_first().eq([1, 2, 3].into_iter()))
}
#[test]
fn test_tuple_second() {
let v = vec![(1, 'a'), (2, 'b'), (3, 'b')];
assert!(v.iter().tuple_second().eq(['a', 'b', 'b'].into_iter()))
}
评论
share
tuple_second()
tuple_third()
Mapper2
Mapper3
在 Rust 1.75 版本中,您将能够编写为返回类型。这将使您的尝试 1(当修复正确性时)工作不变。唯一的其他效果是您需要从中删除该类型,因为它现在是不透明的。impl Trait
m
我简化了你的特征,使内容更易于阅读,但这种技术也适用于所有泛型的原始特征。特别是,您可能希望在特征中保留泛型。T1
trait TupleExtractor {
type TupleItem;
fn tuple_first(self) -> impl Iterator<Item = Self::TupleItem>;
}
impl<'a, T1, T2, I> TupleExtractor for I
where
I: Iterator<Item = &'a (T1, T2)>,
T1: Copy + 'a,
T2: 'a,
{
type TupleItem = T1;
fn tuple_first(self) -> impl Iterator<Item = Self::TupleItem> {
self.map(|e| e.0)
}
}
1.75 将于 2023 年底推出。一般概念称为“返回位置 impl trait in trait”(RPITIT),专门用于将异步特征脱糖为 。你今天就可以使用 Rust 的测试版或夜间版本来尝试这个。impl Future
评论
impl Trait