Объекты трейтов

Последнее обновление: 07.05.2024

Объекты трейтов (trait objects) в языке Rust позволяют выбирать конкретную реализацию трейта во время выполнения. При использовании объектов трейтов по сути применяется динамическая диспетчеризация — процесс, который во время выполнения определяет, какую реализацию метода следует вызвать на основе фактического типа объекта. В этом отличие от статической диспетчеризации, где вызовы методов разрешаются во время компиляции.

Для определения объекта трейта применяется слово dyn

let переменная: &dyn трейт

Рассмотрим пример:

trait Shape {
    fn area(&self) -> f64; // метод вычисления площади фигуры
}

// структура круг
struct Circle {
    radius: f64 // радиус
}
// структура прямоугольник
struct Rectangle {
    width: f64,     // ширина
    height: f64     // высота
}

// реализации вычисления площади для круга
impl Shape for Circle {
    fn area(&self) -> f64 {
        self.radius * self.radius * 3.14
    }
}
// реализации вычисления площади для прямоугольника
impl Shape for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

fn main() {
    let mut my_shape: &dyn Shape = &Circle { radius: 5.0 }; // определяем трейт объект, который хранит ссылку на структуру Circle
    println!("Circle area: {}", my_shape.area()); // вызываем метод area у объекта трейта my_shape

    my_shape = &Rectangle {width:10.0, height: 20.0};   // изменяем значение объекта трейта
    println!("Rectangle area: {}", my_shape.area());
}

Здесь у нас есть трейт Shape, который определяет геометрическую фигуру и метод для вычисления ее площади.

Также есть две структуры - Circle и Rectangle, которые представляют круг и прямоугольник соответственно и которые по своему реализуют трейт Shape.

В функции main мы создаем объект трейта my_shape, который представляет изменяемую переменную - объект трейта Shape. Для его определения применяется ключевое слово dyn

let mut my_shape: &dyn Shape = &Circle { radius: 5.0 };

Эта переменная инициализируется ссылкой на объект Circle. Стоит отметить, что для целей демонстрации здесь переменная определена как изменяемая, но в принципе это не обязательно. Главное, что она представляет трейт Shape. И, как и в общем случае, далее мы можем вызвать у нее метод area:

println!("Circle area: {}", my_shape.area()); // вызываем метод area у объекта трейта my_shape

И поскольку переменная является изменяемой, то мы можем присвоить ей ссылку на другой объект, который реализует трейт Shape, например, ссылку на Rectangle:

my_shape = &Rectangle {width:10.0, height: 20.0};

И далее также можем вызывать метод area.

Стоит отметить, что мы не можем просто присвоить переменной ссылку на Circle, а потом на Rectangle:

let mut my_shape = &Circle { radius: 5.0 };
........................
my_shape = &Rectangle {width:10.0, height: 20.0}; // ! Ошибка

В этом случае мы получим ошибку, так как тип переменной сразу определяется как Circle.

Аналогичным образом мы можем использовать объекты трейтов в функциях в качестве параметров:

trait Shape {
    fn area(&self) -> f64; 
}

struct Circle {
    radius: f64
}

struct Rectangle {
    width: f64,
    height: f64
}


impl Shape for Circle {
    fn area(&self) -> f64 {
        self.radius * self.radius * 3.14
    }
}

impl Shape for Rectangle {
    fn area(&self) -> f64 {
        self.width * self.height
    }
}

fn print_area(shape: &dyn Shape) { // shape - объект трейта Shape

    println!("Area: {}", shape.area());
}

fn main() {
    let circle = Circle { radius: 5.0 }; 
    print_area(&circle);    // Area: 78.5

    let rect = Rectangle {width:10.0, height: 20.0};
    print_area(&rect);  // Area: 200
}

Здесь определена функция print_area, которая принимает объект трейта Shape и выводит его площадь.

Объекты трейтов позволяют добиться полиморфизма во время выполнения, делая код более гибким и адаптируемым к различным типам, сохраняя при этом общий признак. В то же время эта гибкость и динамическая диспетчеризация приводит к незначительному снижению производительности по сравнению со статической диспетчеризацией.

Помощь сайту
Юмани:
410011174743222
Номер карты:
4048415020898850