rust 返回关联类型的trait函数的默认实现

s4n0splo  于 2023-08-05  发布在  其他
关注(0)|答案(3)|浏览(103)

我正在为我的trait函数定义一个返回迭代器的默认实现。
我定义了一个trait(Labels),它有两个函数(labels_veclabels_iterator)。我还有一个实现该trait的struct(NumberThing)。看下面的小例子:

use std::{iter::Map, ops::RangeInclusive};

trait Labels {
    type Iterator: Iterator<Item = String>;

    // This trait function has a default implementation.
    fn labels_vec(&self) -> Vec<String> {
        vec!["default".to_string()]
    }

    // Can this trait function also have a default implementation? How?
    fn labels_iterator(&self) -> Self::Iterator;
}

struct NumberThing {
    max: u8,
}

impl Labels for NumberThing {
    type Iterator = Map<RangeInclusive<u8>, fn(u8) -> String>;

    fn labels_iterator(&self) -> Self::Iterator {
        (0..=self.max).map(|number| number.to_string())
    }
}

pub fn main() {
    let number_thing = NumberThing { max: 3 };
    let labels_from_iterator: Vec<String> = number_thing.labels_iterator().collect();
    println!("labels from iterator: {labels_from_iterator:?}");
    println!("labels from vec: {:?}", number_thing.labels_vec());
}

字符串
如何为labels_iterator添加默认实现?

rqmkfv5c

rqmkfv5c1#

你不能这么做
好吧,如果你真的想,那么你可以,在夜间,通过转发逻辑到另一种类型...但别这样求你了。

#![feature(associated_type_defaults)]

use std::{iter::Map, ops::RangeInclusive};

trait LabelsIterator<This: ?Sized> {
    type Iterator: Iterator<Item = String>;
    fn labels_iterator_impl(this: &This) -> Self::Iterator;
}

trait Labels {
    type IteratorImpl: LabelsIterator<Self> = DefaultLabelsIterator;
    
    fn max(&self) -> u8;

    fn labels_iterator(&self) -> <Self::IteratorImpl as LabelsIterator<Self>>::Iterator {
        Self::IteratorImpl::labels_iterator_impl(self)
    }
}

enum DefaultLabelsIterator {}

impl<This: ?Sized + Labels> LabelsIterator<This> for DefaultLabelsIterator {
    type Iterator = Map<RangeInclusive<u8>, fn(u8) -> String>;

    fn labels_iterator_impl(this: &This) -> Self::Iterator {
        (0..=this.max()).map(|number| number.to_string())
    }
}

struct NumberThing {
    max: u8,
}

// impl LabelsIterator<NumberThing> for NumberThing {
//     type Iterator = Map<RangeInclusive<u8>, fn(u8) -> String>;

//     fn labels_iterator_impl(this: &Self) -> Self::Iterator {
//         (0..=this.max).map(|number| number.to_string())
//     }
// }

impl Labels for NumberThing {
    // type IteratorImpl = Self;
    
    fn max(&self) -> u8 {
        self.max
    }
}

pub fn main() {
    let number_thing = NumberThing { max: 3 };
    let labels_from_iterator: Vec<String> = number_thing.labels_iterator().collect();
    println!("labels from iterator: {labels_from_iterator:?}");
}

字符串

v8wbuo2f

v8wbuo2f2#

只有当关联类型提供了某种方式来构造自己时,例如通过实现Default trait:

trait Labels {
    type Iterator: Iterator<Item = String> + Default;

    fn labels_iterator(&self) -> Self::Iterator {
        Self::Iterator::default()
    }
}

字符串
但这似乎没什么用。如果你想用默认impls中的类型做更复杂的事情,关联类型可能不是正确的解决方案。

zi8p0yeb

zi8p0yeb3#

Chayim Friedman's answer让我走上了正确的道路。为了将来的参考,我将把我最后得到的代码放在下面。
即使代码片段使用rust nightly通道,也很容易修改它以使其在稳定通道中工作。

#![feature(associated_type_defaults)]

use std::{iter::Map, ops::RangeInclusive, vec::IntoIter};

trait Labels {
    // Only this line requires `#![feature(associated_type_defaults)]`.
    // In the stable channel (1.71) this solution is also possible,
    // just add a line like this one to each `Labels` implementation
    // that relies on the default implementation of `labels_iterator`.
    type IteratorImpl: LabelsIterator = DefaultLabelsIterator;

    // This trait function can have a default implementation.
    fn labels_vec(&self) -> Vec<String> {
        vec!["default vec impl".to_string()]
    }

    // This trait function now also has a default implementation! :-D
    fn labels_iterator(&self) -> <Self::IteratorImpl as LabelsIterator>::Iterator {
        Self::IteratorImpl::labels_iterator_impl()
    }
}

trait LabelsIterator {
    type Iterator: Iterator<Item = String>;
    fn labels_iterator_impl() -> Self::Iterator;
}

enum DefaultLabelsIterator {}

impl LabelsIterator for DefaultLabelsIterator {
    type Iterator = IntoIter<String>;

    fn labels_iterator_impl() -> Self::Iterator {
        vec!["default iter impl".to_string()].into_iter()
    }
}

struct NumberThing {
    max: u8,
}

impl Labels for NumberThing {
    // NumberThing now relies on the default implementations of the trait functions.
    // Uncomment the lines below to override with custom implementations using `self.max`.

    // type IteratorImpl = NumberThingIterator;

    // fn labels_vec(&self) -> Vec<String> {
    //     (0..=self.max).map(|number| number.to_string()).collect()
    // }

    // fn labels_iterator(&self) -> <Self::IteratorImpl as LabelsIterator>::Iterator {
    //     (0..=self.max).map(|number| number.to_string())
    // }
}

enum NumberThingIterator {}

impl LabelsIterator for NumberThingIterator {
    type Iterator = Map<RangeInclusive<u8>, fn(u8) -> String>;

    // Using NumberThingIterator requires you to override the default implementation `labels_iterator`.
    // This cannot be enforced by the compiler, which is sad :-(
    fn labels_iterator_impl() -> Self::Iterator {
        panic!("override default `labels_iterator`")
    }
}

pub fn main() {
    let number_thing = NumberThing { max: 3 };
    let labels_from_iterator: Vec<String> = number_thing.labels_iterator().collect();
    println!("labels from iterator: {labels_from_iterator:?}");
    println!("labels from vec: {:?}", number_thing.labels_vec());
}

字符串

相关问题