如何在不重复方法的情况下为结构实现多个特征?

时间:2015-12-14 14:55:17

标签: rust traits

下面的代码运行得很好,但我重复了很多 而且我认为这不是真的很乡村。例如,我正在为Square实现两个特征,这感觉不对!此外,函数coordinate()在特征和实现中重复。

有没有办法实现这段代码而不经常重复自己?是否可以实现以下两个特征:

impl BasicInfo && Sides for Square {
    ....
} 

上面的代码不起作用,这只是一个想法。当函数可以应用于多个结构时,是否可以在特征BasicInfo中仅定义一次并访问它。

fn main() {
    let x_1: f64 = 2.5;
    let y_1: f64 = 5.2;
    let radius_1: f64 = 5.5;
    let width_01 = 10.54;

    let circle_01 = Circle { x: x_1, y: y_1, radius: radius_1 };
    let square_01 = Square { x: x_1, y: y_1, width: width_01, sides: 4 };

    println!("circle_01 has an area of {:.3}.", 
        circle_01.area().round());
    println!("{:?}", circle_01);
    println!("The coordinate of circle_01 is {:?}.\n", circle_01.coordinate());
    println!("coordinate of square_01: {:?} has an area of: {} m2 and also has {} sides.", 
        square_01.coordinate(), 
        (square_01.area() * 100.0).round() / 100.0,
        square_01.has_sides() );
}

#[derive(Debug)]
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

struct Square {
    x: f64,
    y: f64,
    width: f64,
    sides: i32,
}

trait BasicInfo {
    fn area(&self) -> f64;
    // coordinate() is declared here, but not defined. Is it possible to define it here and still be able to access it when I want it.
    fn coordinate(&self) -> (f64, f64);
}

trait Sides {
    fn has_sides(&self) -> i32;
}

impl BasicInfo for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
    // coordinate() gets defined again, and looks like repeating code
    fn coordinate(&self) -> (f64, f64) {
        (self.x, self.y)
    }
}

impl BasicInfo for Square {
    fn area(&self) -> f64 {
        self.width.powf(2.0)
    }
    // coordinate() gets defined again, and looks like repeating code
    fn coordinate(&self) -> (f64, f64) {
        (self.x, self.y)
    }
}

impl Sides for Square {
    fn has_sides(&self) -> i32 {
        self.sides
    }
}

3 个答案:

答案 0 :(得分:4)

第二个问题(避免重复coordinate的相同实现)我想向您展示基于宏的解决方案。

有趣的是,它留下了3个特征而不是2个特征,所以它与你的第一个问题完全相反。我想你不能拥有一切! :)

// factoring out the Coordinates trait from BasicInfo
trait Coordinates {
    fn coordinate(&self) -> (f64, f64);
}

// but we can require implementors of BasicInfo to also impl Coordinates
trait BasicInfo: Coordinates {
    fn area(&self) -> f64;
}

// helper macro to avoid repetition of "basic" impl Coordinates
macro_rules! impl_Coordinates { 
    ($T:ident) => {
        impl Coordinates for $T {
            fn coordinate(&self) -> (f64, f64) { (self.x, self.y) }
        }
    }
}

#[derive(Debug)]
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

#[derive(Debug)]
struct Square {
    x: f64,
    y: f64,
    width: f64,
    sides: i32,
}

// the macro here will expand to identical implementations
// for Circle and Square. There are also more clever (but a bit
// harder to understand) ways to write the macro, so you can
// just do impl_Coordinates!(Circle, Square, Triangle, OtherShape)
// instead of repeating impl_Coordinates!
impl_Coordinates!(Circle);
impl_Coordinates!(Square);


trait Sides {
    fn has_sides(&self) -> i32;
}

impl BasicInfo for Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}

impl BasicInfo for Square {
    fn area(&self) -> f64 {
        self.width.powf(2.0)
    }
}

impl Sides for Square {
    fn has_sides(&self) -> i32 {
        self.sides
    }
}

答案 1 :(得分:1)

为一种类型实现多个特征没有错;事实上它很常见。

我也不会通过重复coordinate这个特性和impl来理解你的意思。该函数在trait中声明并在impl中实现,就像所有其他trait函数一样。你的意思是Square和Circle impls中函数的实现是一样的吗? Macros会帮助你,尽管可能有更好的方法。

答案 2 :(得分:1)

免责声明:在撰写本文时,有两个问题捆绑在一起,而后者与标题不符。所以按照标题......

  

是否可以一次实现多个特征?

impl BasicInfo && Sides for Square {
    ....
}

单独实现它们的开销相对较低,对于更复杂的情况,当需要约束时,可能无法为每个特征设置不同类型的约束。

话虽这么说,你可以打开一个RFC建议实现这个,然后让社区/开发者决定他们是否觉得值得实现。