简体   繁体   English

有没有办法创建基于特征实现的结构?

[英]Is there a way to create a struct based on a trait implementation?

I'm trying to use a struct with multiple implementations of one method:我正在尝试使用具有一种方法的多种实现的结构:

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();
}

Which would return:哪个会返回:

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

I know I could create 2 structs implementing these methods differently, but that feels wrong as it's potentially a lot of redundant code.我知道我可以创建 2 个结构体以不同的方式实现这些方法,但这感觉不对,因为它可能有很多冗余代码。 I also know I could have a reference to that implementation method:我也知道我可以参考该实现方法:

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);
}

This approach seems needlessly complicated when I would want to use many arguments inside given struct:当我想在给定的结构中使用许多参数时,这种方法似乎不必要地复杂:

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, ...);
}

This seems to be a cursed way of writing code.这似乎是一种被诅咒的编写代码的方式。 What happens when a method implementation doesn't use a parameter eg m , and other uses that?当方法实现不使用参数(例如m )而其他人使用该参数时会发生什么?

Traits are used to define shared behaviour.特征用于定义共享行为。 In your example, you want to implement the same trait in different ways.在您的示例中,您希望以不同的方式实现相同的特征。 This goes against the purpose of a trait.这违背了 trait 的目的。 Instead of having two structs as you tried, you should probably have two traits:你应该有两个特征,而不是像你尝试的那样有两个结构:

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

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

Now you can implement both traits for your struct:现在你可以为你的结构实现这两个特征:

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()
    }
}

Because Bar implements ThisWay and ThatWay , it now has two definitions for the apply method.因为Bar实现了ThisWayThatWay ,它现在有两个apply方法的定义。 To disambiguate between them, we have to use Fully Qualified Syntax:为了消除它们之间的歧义,我们必须使用完全限定的语法:

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));

And, as expected, you get two different outputs:而且,正如预期的那样,您会得到两个不同的输出:

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

As an alternative to the other answer, you can also use something more similar to your original example, using generics and zero-sized struct types as "markers" for which method you want to use.作为另一个答案的替代方案,您还可以使用更类似于原始示例的方法,使用泛型和零大小的结构类型作为要使用的方法的“标记”。 Here's a complete example:这是一个完整的例子:

// 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();
}

Running this, the output correctly reflects the different functions being used:运行这个,输出正确地反映了正在使用的不同功能:

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

This approach has different semantics from the other answer: whereas the other answer allows you to construct a Bar that's capable of being used with both functions, this approach locks you into one implementation at the type level, since Bar<ThisWay> and Bar<ThatWay> are two separate types that each only provide only one apply function.这种方法与另一个答案具有不同的语义:而另一个答案允许您构造一个能够与这两个函数一起使用的Bar ,这种方法将您锁定在类型级别的一个实现中,因为Bar<ThisWay>Bar<ThatWay>是两种不同的类型,每一种都只提供一个apply功能。 This can be desirable for type safety in some scenarios, but may not be what you need for this particular case.在某些情况下,这对于类型安全来说可能是可取的,但在这种特殊情况下可能不是您所需要的。

// since x is declared as `Bar<ThatWay>`, we can only ever use the `ThatWay` implementation of `apply`/`test`
let x: Bar<ThatWay> = Bar::new(vec![1, 2, 3]);
x.test(); // -> Method: [3, 4, 5]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM