Трейты, протоколы, интерфейсы
// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0
trait Receiver {
fn send(&self, message: &str);
}
struct EmailAddress(String);
impl Receiver for EmailAddress {
fn send(&self, message: &str) {
println!("Email to {}: {}", self.0, message);
}
}
struct ChatId {
uuid: [u8; 16],
}
impl Receiver for ChatId {
fn send(&self, message: &str) {
println!("Chat message sent to {:?}: {}", self.uuid, message);
}
}
-
Концепция полиморфизма и обобщений в Rust во многом построена вокруг трейтов.
-
Трейты — это требования к типу в обобщённом контексте.
-
Такие требования работают почти как утиная типизация, проверяемая на этапе компиляции.
Утиная типизация — понятие из практики динамических нетипизированных языков вроде Python: «если оно ходит как утка и крякает как утка, то это утка».
То есть любой тип с методами и полями, которые ожидает функция, — допустимый аргумент для этой функции. Если тип реализует нужные методы, в контексте утиной типизации он считается подходящим.
Трейты работают как статический механизм утиной типизации: мы задаём поведение, а не тип. При этом мы получаем проверку на этапе компиляции, действительно ли это поведение существует.
-
Или иначе: трейты похожи на наборы утверждений, а реализация трейта для типа — это доказательство того, что тип можно использовать везде, где требуется этот трейт.
У трейтов есть обязательные методы; реализация этих методов — доказательство того, что тип обладает нужным поведением.
Справка:
- https://doc.rust-lang.org/reference/items/traits.html