有没有办法根据特征实现创建结构?
Is there a way to create a struct based on a trait 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();
}
哪个 return:
>>> [1,4,9];
>>> [3,4,5];
我知道我可以创建 2 个以不同方式实现这些方法的结构,但感觉不对,因为它可能有很多冗余代码。
我也知道我可以参考那个实现方法:
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, ...);
}
这似乎是一种被诅咒的代码编写方式。当方法实现不使用参数时会发生什么,例如 m
,而其他人使用该参数?
特征用于定义共享行为。在您的示例中,您想以不同的方式实现相同的特征。这违背了特质的目的。而不是像你尝试的那样有两个结构,你应该有两个特征:
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
实现了 ThisWay
和 ThatWay
,它现在有两个 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]
作为其他答案的替代方案,您还可以使用与原始示例更相似的内容,使用泛型和 zero-sized 结构类型作为您要使用的方法的“标记”。这是一个完整的例子:
// 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
,这种方法将您锁定在类型级别的一个实现中,因为 Bar<ThisWay>
和 Bar<ThatWay>
是两种不同的类型,每个类型只提供一个 apply
功能。在某些情况下,这对于类型安全可能是可取的,但对于这种特定情况可能不是您所需要的。
// 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]
我正在尝试使用具有一种方法的多个实现的结构:
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();
}
哪个 return:
>>> [1,4,9];
>>> [3,4,5];
我知道我可以创建 2 个以不同方式实现这些方法的结构,但感觉不对,因为它可能有很多冗余代码。 我也知道我可以参考那个实现方法:
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, ...);
}
这似乎是一种被诅咒的代码编写方式。当方法实现不使用参数时会发生什么,例如 m
,而其他人使用该参数?
特征用于定义共享行为。在您的示例中,您想以不同的方式实现相同的特征。这违背了特质的目的。而不是像你尝试的那样有两个结构,你应该有两个特征:
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
实现了 ThisWay
和 ThatWay
,它现在有两个 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]
作为其他答案的替代方案,您还可以使用与原始示例更相似的内容,使用泛型和 zero-sized 结构类型作为您要使用的方法的“标记”。这是一个完整的例子:
// 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
,这种方法将您锁定在类型级别的一个实现中,因为 Bar<ThisWay>
和 Bar<ThatWay>
是两种不同的类型,每个类型只提供一个 apply
功能。在某些情况下,这对于类型安全可能是可取的,但对于这种特定情况可能不是您所需要的。
// 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]