Ассоциированные типы (associated types) в языке Rust позволяют абстрагироваться от конретных типов и назначить им псевдоним. Подобный уровень абстракции обеспечивает гибкость и расширяемость, что делает ассоциированные типы ценным инструментом при проектировании интерфейсов для сложных систем.
Для определения ассоциированного типа в трейте применяется ключевое слово type, после которого указывается псевдоним типа:
trait название_трейта{
type псевдоним_типа;
// остальной функционал трейта
}
Рассмотрим простейший пример:
struct Circle{ radius:f64}
struct Rectangle {width:u32, height:u32}
trait Shape{
type Unit; // ассоциированный тип
fn area(&self)->Self::Unit;
}
impl Shape for Circle{
type Unit = f64;
fn area(&self) -> Self::Unit { self.radius * self.radius * 3.14 }
}
impl Shape for Rectangle{
type Unit = u32;
fn area(&self) -> Self::Unit { self.width * self.height }
}
fn main() {
let circle = Circle{radius:10.0};
println!("Area = {}", circle.area()); // Area = 314
let rect = Rectangle{width:10, height:20};
println!("Area = {}", rect.area()); // Area = 200
}
Здесь у нас определены структуры Circle и Rectangle , которые представляют круг и прямоугольник соответственно. И также определен трейт Shape, который определяет общую функциональность для геометрических фигур:
trait Shape{
type Unit;
fn area(&self)->Self::Unit;
}
Здесь определяется ассоциированный тип с псевдонимом "Unit". На момент определения неизвестно, что это будет в реальности за тип. Но единственное, что известно,
это то, что функция area, которая вычисляет площадь фигуры, будет возвращать значение этого типа. Для ссылки на этот тип внутри трейта применяется выражение
Self::Unit (формально Self::псевдоним)
Структуры Circle и Rectangle по своему реализуют этот трейт и используют разные ассоциированные типы. Например, для Circle в качестве ассоциированного типа выступает тип f64 (64-разрядное число с плавающей точкой):
impl Shape for Circle{
type Unit = f64;
fn area(&self) -> Self::Unit { self.radius * self.radius * 3.14 }
}
А для структуры Rectangle ассоциированный тип - это u32:
impl Shape for Rectangle{
type Unit = u32;
fn area(&self) -> Self::Unit { self.width * self.height }
}
Также при определении ассоциированного типа мы можем явно задать трейт типа, которому ассоциированный тип должен соответствовать. Например:
struct Circle;
struct Rectangle;
trait Shape {}
impl Shape for Circle{ }
impl Shape for Rectangle{ }
struct CircleGeometry;
struct RectangleGeometry;
trait Geometry{
type ShapeType: Shape;
fn create_shape()-> Self::ShapeType;
}
impl Geometry for CircleGeometry{
type ShapeType = Circle;
fn create_shape() -> Self::ShapeType { Circle}
}
impl Geometry for RectangleGeometry{
type ShapeType = Rectangle;
fn create_shape() -> Self::ShapeType { Rectangle}
}
Здесь в трейте Geometry ассоцированный тип ShapeType обязательно должен представлять тип Shape (структуру, которая реализует данный трейт). А реализации трейта Geometry - CircleGeometry и RectangleGeometry устанавливают конкретный тип для псевдонима ShapeType.