Классический полиморфизм обеспечивается отношением между родительским и наследуемым типом, когда мы можем ссылаться на наследника через родительский тип. Выборочно выполнять методы родителя или наследника с помощью кастования типов. Или можем создать абстрактный тип с виртуальными функциями для наследников, которые будут автоматически выбираться во время выполнения. Как это делается в Rust поговорим более подробно.
Статический полиморфизм - полиморфизм времени компиляции базируется на основе работы генериков и ручного кастования объектов в нужный тип. И так как в Rust нет наследования, родительский тип и его привязка к наследникам задается с помощью глобального типа Any (похоже на суперкласс Object из Java) или характеристик, которые мы будем рассматривать далее. При этом создаются копии методов с нужным типом при компиляции и это не требует дополнительных действий при выполнении, что повышает скорость работы программы.
Динамический полиморфизм - полиморфизм времени выполнения базируется на основе динамического преобразования типов во время выполнения. Задается с помощью ключевого слова “dyn” в определении ссылки на Any или характеристику. Так как выбор типа и определение нужного метода происходит во время выполнения приложения, это замедляет его работу. Учитывая, что в работе с характеристиками требуется динамическое кастование, которое тоже нагружает систему, можно отдать предпочтение удобству.
Итак, что же такое характеристика (trait)? Образно говоря, это гарантия способности выполнять определенные действия. В нашем контексте это гарантия наличия у типа определенных методов, которые мы можем использовать. Rust позволяет обращаться к типам, которые реализуют (impl - for) характеристику через тип характеристики (&dyn), как обращение к наследникам через базовый тип в ООП. Более того, мы можем цеплять характеристику к чужим (базовым в том числе) типам или требовать (where) от чужих типов ее наличие. Например, чтобы определить оператор "плюс" (+) для пользовательского типа, мы должны гарантировать определение метода add() из характеристики Add, которая находится в стандартной библиотеке (std::ops). Нужно отметить один момент, мы можем потребовать определить для характеристики тип(ы), с помощью type. Смотрим пример.
shape.rs
-----------
use std::ops::Add;
pub trait Drawable {
fn draw(&self);
}
pub fn generic_draw<T>(value: T) where T: Drawable {
value.draw();
}
#[derive(Debug)]
pub struct Point {
pub x: i32,
pub y: i32,
}
impl Add for Point{
type Output = Point;
fn add(self, rhs: Self) -> Self::Output {
Point{
x:self.x + rhs.x,
y:self.y + rhs.y,
}
}
}
circle.rs
------------
use super::shape::*;
#[derive(Debug)]
pub struct Circle {
radius: i32,
center: Point,
}
impl Circle {
pub fn new() -> Self {
Circle {
radius: 1,
center: Point {
x: 0,
y: 0,
},
}
}
}
impl Drawable for Circle {
fn draw(&self) {
println!("Draw circle: {:?}", self)
}
}
triangle.rs
-------------
use super::shape::Drawable;
#[derive(Debug)]
pub struct Triangle {
width: i32,
heiht: i32,
}
impl Triangle {
pub fn new() -> Self {
Triangle {
width: 5,
heiht: 5,
}
}
}
impl Drawable for Triangle {
fn draw(&self) {
println!("Draw triangle: {:?}", self)
}
}
main.rs
-----------
mod shape;
mod circle;
mod triangle;
use shape::*;
use circle::*;
use triangle::*;
use std::any::Any;
fn main() {
let a = Point{x: 1, y: 1};
let b = Point{x: 2, y: 2};
let c = a + b;
println!("Add operator: a + b = {:?}", c);
let c1 = Circle::new();
let t1 = Triangle::new();
let objects: Vec<&dyn Any> = vec![&c1, &t1];
for object in objects.iter() {
if object.is::<Circle>() {
println!("Circle:");
} else if object.is::<Triangle>() {
println!("Triangle:");
}
if let Some(circle) = object.downcast_ref::<Circle>() {
circle.draw();
} else if let Some(triangle) = object.downcast_ref::<Triangle>() {
triangle.draw();
}
}
//dynamic polymorphism
let shapes: Vec<&dyn Drawable> = vec![&c1, &t1];
for shape in shapes.iter() {
shape.draw();
}
//static polymorphism
generic_draw(c1);
generic_draw(t1);
}
Перегуд В.
Комментариев нет:
Отправить комментарий