Выложил статью. Предлагаю читать-комментировать.
В статье предлагается реализовать трейт Figure вручную для Square, Rectangle.
Такие трейты можно делать автоматически выводимыми:
trait Figure: Area + fmt::Display { }
impl<T: Area + fmt::Display> Figure for T { }
Тогда не придется писать
impl Figure for Rectangle
Спасибо за замечание.
Видимо, это то, что называется “Blanket Impl”.
А как правильно организовать фаловую структуру для проекта с таким наследованием?
В классических языках обычно один класс - один файл, где реализован весь его функционал. А тут появляется еще и типаж, который содержит в себе часть функционала класса - получается распыление кода. Или я что-то не понимаю, и можно обособить код, относящийся к конкретному классу?
Смотрите пример в репозитории.
Вариант с динамическим преобразованием к общему типажу-объекту, чтобы не дублировать функционал при наличии нескольких веток иерархии:
mod figures {
use std::fmt;
pub trait Fig {
fn as_rect(&self) -> Option<&Rect> {
None
}
fn as_ellip(&self) -> Option<&Ellip> {
None
}
fn area(&self) -> f64 {
if let Some(rect) = self.as_rect() {
rect.width() * rect.length()
} else if let Some(ellip) = self.as_ellip() {
::std::f64::consts::PI * ellip.a() * ellip.b()
} else {
0.
}
}
}
pub trait Rect {
fn width(&self) -> f64;
fn length(&self) -> f64;
}
pub trait Ellip {
fn a(&self) -> f64;
fn b(&self) -> f64;
}
/**
* Rectangle
**/
pub struct Rectangle {
width: f64,
length: f64,
}
impl Rectangle {
pub fn new(width: f64, length: f64) -> Option<Rectangle> {
if width > 0. && length > 0. {
Some(Rectangle { length, width })
} else {
None
}
}
}
impl Rect for Rectangle {
fn length(&self) -> f64 {
self.length
}
fn width(&self) -> f64 {
self.width
}
}
impl Fig for Rectangle {
fn as_rect(&self) -> Option<&Rect> {
Some(self)
}
}
impl fmt::Display for Rectangle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "прямоугольник({}, {})", self.width, self.length)
}
}
/**
* Square
**/
pub struct Square {
side: f64,
}
impl Square {
pub fn new(side: f64) -> Option<Square> {
if side > 0. {
Some(Square { side })
} else {
None
}
}
}
impl Rect for Square {
fn length(&self) -> f64 {
self.side
}
fn width(&self) -> f64 {
self.side
}
}
impl Fig for Square {
fn as_rect(&self) -> Option<&Rect> {
Some(self)
}
}
impl fmt::Display for Square {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "квадрат({})", self.side)
}
}
/**
* Ellipse
**/
pub struct Ellipse {
a: f64,
b: f64,
}
impl Ellipse {
pub fn new(a: f64, b: f64) -> Option<Ellipse> {
if a > 0. && b > 0. {
Some( Ellipse { a, b } )
} else {
None
}
}
}
impl Ellip for Ellipse {
fn a(&self) -> f64 {
self.a
}
fn b(&self) -> f64 {
self.b
}
}
impl Fig for Ellipse {
fn as_ellip(&self) -> Option<&Ellip> {
Some(self)
}
}
impl fmt::Display for Ellipse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "эллипс({}, {})", self.a, self.b)
}
}
/**
* Circle
**/
pub struct Circle {
radius: f64,
}
impl Circle {
pub fn new(radius: f64) -> Option<Circle> {
if radius > 0. {
Some( Circle { radius } )
} else {
None
}
}
}
impl Ellip for Circle {
fn a(&self) -> f64 {
self.radius
}
fn b(&self) -> f64 {
self.radius
}
}
impl Fig for Circle {
fn as_ellip(&self) -> Option<&Ellip> {
Some(self)
}
}
impl fmt::Display for Circle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "круг({})", self.radius)
}
}
}
use std::fmt;
use figures::{Fig, Rectangle, Square, Ellipse, Circle};
trait Figure: Fig + fmt::Display {}
impl<T: Fig + fmt::Display> Figure for T {}
fn print_figures_and_areas(figures: &[&Figure]) {
for f in figures.iter() {
println!("Площадь {} равна {}", f, f.area());
}
}
fn main() {
let rect1 = Rectangle::new(3., 5.).unwrap();
let rect2 = Rectangle::new(4., 6.).unwrap();
let sq1 = Square::new(8.).unwrap();
let sq2 = Square::new(4.).unwrap();
let ellipse1 = Ellipse::new(1., 2.).unwrap();
let ellipse2 = Ellipse::new(2., 4.).unwrap();
let circle1 = Circle::new(1.).unwrap();
let circle2 = Circle::new(2.).unwrap();
print_figures_and_areas(
&[&rect1, &rect2, &sq1, &sq2, &ellipse1, &ellipse2, &circle1, &circle2]
);
}
https://play.rust-lang.org/?gist=e815824b01c58d7e2043b51b817d188c&version=stable&mode=debug
Еще динамический вариант с использованием Box<dyn Fig>
mod figures {
use std::fmt;
pub trait Fig: fmt::Display {
fn area(&self) -> f64;
}
/**
* Rectangle
**/
pub struct Rectangle {
width: f64,
length: f64,
}
impl Rectangle {
pub fn new(width: f64, length: f64) -> Option<Rectangle> {
if width > 0. && length > 0. {
Some(Rectangle { length, width })
} else {
None
}
}
}
impl Fig for Rectangle {
fn area(&self) -> f64 {
self.width * self.length
}
}
impl fmt::Display for Rectangle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"прямоугольник({}, {})",
self.width, self.length
)
}
}
/**
* Square
**/
pub struct Square {
side: f64,
}
impl Square {
pub fn new(side: f64) -> Option<Square> {
if side > 0. {
Some(Square { side })
} else {
None
}
}
}
impl Fig for Square {
fn area(&self) -> f64 {
self.side * self.side
}
}
impl fmt::Display for Square {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "квадрат({})", self.side)
}
}
/**
* Ellipse
**/
pub struct Ellipse {
a: f64,
b: f64,
}
impl Ellipse {
pub fn new(a: f64, b: f64) -> Option<Ellipse> {
if a > 0. && b > 0. {
Some(Ellipse { a, b })
} else {
None
}
}
}
impl Fig for Ellipse {
fn area(&self) -> f64 {
::std::f64::consts::PI * self.a * self.b
}
}
impl fmt::Display for Ellipse {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "эллипс({}, {})", self.a, self.b)
}
}
/**
* Circle
**/
pub struct Circle {
radius: f64,
}
impl Circle {
pub fn new(radius: f64) -> Option<Circle> {
if radius > 0. {
Some(Circle { radius })
} else {
None
}
}
}
impl Fig for Circle {
fn area(&self) -> f64 {
::std::f64::consts::PI * self.radius * self.radius
}
}
impl fmt::Display for Circle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "круг({})", self.radius)
}
}
}
use figures::{Circle, Ellipse, Fig, Rectangle, Square};
fn main() {
let mut figures: Vec<Box<dyn Fig>> = Vec::new();
figures.push(Box::from(Rectangle::new(3., 5.).unwrap()));
figures.push(Box::from(Rectangle::new(4., 6.).unwrap()));
figures.push(Box::from(Square::new(8.).unwrap()));
figures.push(Box::from(Square::new(4.).unwrap()));
figures.push(Box::from(Ellipse::new(1., 2.).unwrap()));
figures.push(Box::from(Ellipse::new(2., 4.).unwrap()));
figures.push(Box::from(Circle::new(1.).unwrap()));
figures.push(Box::from(Circle::new(2.).unwrap()));
for f in figures.iter() {
println!("Площадь {} равна {}", f, f.area());
}
}