Программирование на уровне типа (type-level programming) представляет концепцию, которая использует систему типов для инкапсуляции сложной логики и взаимосвязей между типами. Программирование на уровне типа включает в себя создание ограничений на уровне типа, что позволяет определять сложные условия, которым должны удовлетворять типы. В Rust для создания ограничений применяются трейты.
Вкратце рассмотрим концепцию на следующем примере:
// трейт персонажа в игре
trait Character {
type WeaponType: Weapon; // оружие, ассоциированое с персонажем
// метод создания оружия, которое ассоциировано с персонажем
fn create_weapon() -> Self::WeaponType;
}
// трейт оружия
trait Weapon {
fn attack(&self);
}
// структура воина
struct Warrior;
// структура мага
struct Mage;
// структура, которая представляет меч
struct Sword;
// структура, которая представляет посох
struct Staff;
// реализация меча
impl Weapon for Sword {
fn attack(&self) {
println!("Атакуем мечом");
}
}
// реализация для посоха
impl Weapon for Staff {
fn attack(&self) {
println!("Применяем магию");
}
}
// реализация воина
impl Character for Warrior {
type WeaponType = Sword; // оружие - меч
// создаем меч
fn create_weapon() -> Self::WeaponType { Sword }
}
// реализация мага
impl Character for Mage {
type WeaponType = Staff; // оружие - магический посох
// создаем посох
fn create_weapon() -> Self::WeaponType { Staff }
}
// программирование на уровне типа
fn attack<C: Character>() {
let weapon = C::create_weapon(); // создаем оружие ассоциированного типа
weapon.attack(); // применяем оружие
}
fn main() {
attack::<Warrior>();
attack::<Mage>();
}
Вначале определяется трейт Character, который представляет некоторого персонажа в игре. В этом трейте с помощью ассоциированного типа WeaponType устанавливаем оружие персонажа, а ассоциированная функция create_weapon предназначена для создания оружия. Трейт Weapon представляет оружие, которое определяет метод attack для его применения.
Для демонстрации здесь определяется две структуры персонажей - Warrior (воин) и Mage (маг). Для оружия определяется две структуры - Sword (меч) и Staff (посох). И в данном случае наша задача гарантировать, что действительными будут только определенные комбинации классов персонажей и оружия. То есть воин использует меч, а маг - посох.
Структуры Sword и Staff применяют трейт Weapon, определяя реализацию для метода attack. А для структур Warrior и Mage реализуем трейт Character.
Далее определяем функцию верхнего уровня attack, которая применяет программирование на уровне типа:
fn attack<C: Character>() {
let weapon = C::create_weapon(); // создаем оружие ассоциированного типа
weapon.attack();
}
Здесь мы создаем оружие ассоцированного типа для типа C, который представляет структуру игрового персонажа, и далее выполняем метод attack.
В функции main вызываем функцию attack, типизируя ее различными структурами:
attack::<Warrior>(); attack::<Mage>();
Консольный вывод:
Атакуем мечом Применяем магию