Диспетчеризация — это механизм выбора конкретной функции (метода) для вызова. В Rust располагается два подхода: статическая и динамическая диспетчеризация.
История вопроса:
В языках с ООП для динамического вызова метода, как правило, используется vtable (виртуальная таблица). В Rust аналогичное реализовано для trait object — ссылки на объекты типов, реализующих определённые трейты. Статическая диспетчеризация появляется при использовании дженериков и trait bounds.
Проблема:
Часто приходится выбирать между гибкостью (с возможностью работы с объектами разных типов через один интерфейс) и производительностью (статическая диспетчеризация позволяет инлайнить методы). Некорректный выбор приводит либо к избыточно сложным дженерикам, либо к потерям в производительности.
Решение:
Статическая диспетчеризация достигается через generic-параметры: при этом компилятор генерирует отдельный код для каждого типа. Динамическая — если функция принимает аргумент типа &dyn Trait или Box<dyn Trait>, то при вызове метода по трейту Rust смотрит в vtable по адресу, как в классических ООП-языках.
Пример кода:
trait Shape { fn area(&self) -> f64; } impl Shape for Circle { fn area(&self) -> f64 { 3.1415 * self.radius * self.radius } } fn print_area(shape: &dyn Shape) { // dynamic dispatch println!("area = {}", shape.area()); } // Или статически: fn print_area_static<S: Shape>(shape: &S) { println!("area = {}", shape.area()); }
Ключевые особенности:
dyn Trait использует vtable (динамическая диспетчеризация)Можно ли делать Box<dyn Sized>?
Нет. dyn Trait по определению — unsized, всегда требует использования Box, Arc или ссылок, но не «Box<dyn Sized>» — это не имеет смысла. Sized трейтом не обладают trait objects.
Разрешён ли dyn Trait для трейтов с generic-методами?
Нет. Нельзя сделать object-safe трейты с generic-методами (любят путать!), composite типы не object-safe:
trait MyTrait { fn foo<T>(&self, x: T); } let x: &dyn MyTrait = ... // Ошибка компиляции!
Можно ли сделать dyn Trait для трейта с Self-значениями в сигнатуре?
Нельзя, если метод возвращает Self (многие не понимают этот нюанс: object safety требует, чтобы в сигнатуре не было Self; можно self только в аргументах, но не возвращаемым значением).
Везде использовали dyn Trait ради универсальности интерфейсов, даже внутри tight loops, где можно было обойтись generics.
Плюсы:
Минусы:
Статическая диспетчеризация применялась во внутренней логике, а dyn Trait — только на границах модулей.
Плюсы:
Минусы: