有没有一种方法可以基于特征实现创建结构?

时间:2020-11-04 16:07:44

标签: struct rust traits implementation

我正在尝试使用一种方法的多种实现的结构:

\]

哪个会返回:

trait Trait { fn apply(&self) -> vec<usize>; }

struct Bar<X> { vec: Vec<usize> }

impl<X> Bar<X> {
    pub fn new(vec: Vec<usize>) -> Self { Self{vec} }
    pub fn test(&self) {
        // Things here
        println!("Method: {:?}", self.apply()); 
        // Things there
    }
}

impl Trait for Bar<ThisWay> {
    fn apply(&self) -> Vec<usize> { self.vec.iter().map(|x| x.pow(2)).collect() }
}

impl Trait for Bar<ThatWay> {
    fn apply(&self) -> Vec<usize> { self.vec.iter().map(|x| x + 2).collect() }
}

fn main() {
   Bar<ThisWay>::new(vec![1,2,3]).test();
   Bar<ThatWay>::new(vec![1,2,3]).test();
}

我知道我可以创建2个以不同的方式实现这些方法的结构,但这感觉不对,因为它可能包含很多冗余代码。 我也知道我可以参考该实现方法:

>>> [1,4,9];
>>> [3,4,5];

当我想在给定的struct中使用许多参数时,这种方法似乎不必要地复杂:

trait Trait { fn apply(vec: &Vec<usize>) -> Vec<usize>; }

impl Struct{
    // fn new
    test(&self, t: &impl Trait) {
    // Things here
    println!("{:?}", t::apply(&self.vec));
    // Things there
    }
}
struct ThisWay;
struct ThatWay;
impl Trait for ThisWay {fn apply(vec: &Vec<usize>) -> Vec<usize> {///} };
impl Trait for ThatWay {fn apply(vec: &Vec<usize>) -> Vec<usize> {///} };
fn main() {
     let this_way = ThisWay{}; 
     let that_way = ThatWay{};
     let problem = Bar::new(vec![1,2,3]);
     problem.test(&this_way);
     problem.test(&that_way);
}

这似乎是编写代码的一种被诅咒的方式。当方法实现不使用参数fn hill_climber(&self, nullary_op: &impl NullaryOperator, unary_op: &impl UnaryOperator, ...) { self.vec = nullary_op(); self.vec = unary_op(&self.vec, self.n, self.m, self.jobs, self.stuff, ...); } 而其他使用该参数时会发生什么?

2 个答案:

答案 0 :(得分:1)

特质用于定义共享行为。在您的示例中,您想以不同的方式实现相同的特征。这违背了特质的目的。与其尝试使用两个结构,不如说应该具有两个特征:

trait ThisWay {
    fn apply(&self) -> Vec<usize>;
}

trait ThatWay {
    fn apply(&self) -> Vec<usize>;
}

现在您可以为您的结构实现这两个特征:

struct Bar {
    vec: Vec<usize>,
}

impl ThisWay for Bar {
    fn apply(&self) -> Vec<usize> {
        self.vec.iter().map(|x| x.pow(2)).collect()
    }
}

impl ThatWay for Bar {
    fn apply(&self) -> Vec<usize> {
        self.vec.iter().map(|x| x + 2).collect()
    }
}

由于Bar实现了ThisWayThatWay,因此它现在为apply方法提供了两个定义。为了消除它们之间的歧义,我们必须使用完全合格的语法:

let this_bar = Bar::new(vec![1, 2, 3]);
println!("Method: {:?}", <Bar as ThisWay>::apply(&this_bar));
    
let that_bar = Bar::new(vec![1, 2, 3]);
println!("Method: {:?}", <Bar as ThatWay>::apply(&that_bar));

并且,正如预期的那样,您将获得两个不同的输出:

Method: [1, 4, 9]
Method: [3, 4, 5]

答案 1 :(得分:0)

作为其他答案的替代方法,您也可以使用与原始示例更相似的方法,将泛型和零大小的结构类型用作要使用的方法的“标记”。这是一个完整的示例:

// PhantomData allows us to "use" a generic without having an actual field
use std::marker::PhantomData;

// These structs will be used to indicate which implementation we want
struct ThisWay;
struct ThatWay;

trait Trait { fn apply(&self) -> Vec<usize>; }

struct Bar<X> {
    vec: Vec<usize>,
    // This extra field is here to stop the compiler complaining about not using X
    _marker: PhantomData<X>,
}

impl<X> Bar<X> {
    pub fn new(vec: Vec<usize>) -> Self { Self { vec, _marker: PhantomData } }

    // Note the new "where" clause here - we can only implement this function if Bar<X> implements Trait
    pub fn test(&self) where Self: Trait {
        // Things here
        println!("Method: {:?}", self.apply()); 
        // Things there
    }
}

impl Trait for Bar<ThisWay> {
    fn apply(&self) -> Vec<usize> { self.vec.iter().map(|x| x.pow(2)).collect() }
}

impl Trait for Bar<ThatWay> {
    fn apply(&self) -> Vec<usize> { self.vec.iter().map(|x| x + 2).collect() }
}

fn main() {
   Bar::<ThisWay>::new(vec![1,2,3]).test();
   Bar::<ThatWay>::new(vec![1,2,3]).test();
}

运行此命令,输出正确反映所使用的不同功能:

Method: [1, 4, 9]
Method: [3, 4, 5]

此方法与另一个答案具有不同的语义:而另一个答案使您可以构造一个可以同时用于两个函数的Bar,因此,此方法将您锁定为类型级别的一个实现,因为{ {1}}和Bar<ThisWay>是两个单独的类型,每个仅仅提供一个Bar<ThatWay>函数。在某些情况下,这对于类型安全性可能是理想的,但对于这种特殊情况,可能不是您所需要的。

apply