XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Супертрейты
В Rust нет "наследования", но вы можете объявить трейт, который будет надмножеством для другого. Например:
trait Person {
fn name(&self) -> String;
}
// `Student` - супертрейт для `Person`.
// Реализация `Student` требует, чтобы вы также реализовали и `Person`.
trait Student: Person {
fn university(&self) -> String;
}
trait Programmer {
fn fav_language(&self) -> String;
}
// `CompSciStudent` (студент факультета информацики) - супертрейт для `Programmer`
// и `Student`. Реализация `CompSciStudent` требует реализации обоих подтрейтов.
trait CompSciStudent: Programmer + Student {
fn git_username(&self) -> String;
}
fn comp_sci_student_greeting(student: &dyn CompSciStudent) -> String {
format!(
"Меня зовут {} и я посещаю {}. Моё имя в Git {}",
student.name(),
student.university(),
student.git_username()
)
}
fn main() {}
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Смотрите также:
Глава "The Rust Programming Language" о супертрейтах
Устранение неоднозначности в перекрывающихся трейтах
Тип может реализовывать много разных трейтов. Что если два трейта будут требовать метод с одним и тем же именем? например, много трейтов могут иметь метод get(), которые так же могут иметь разные возвращаемые типы!
Хорошие новости: благодаря тому, что каждая реализация трейта имеет собственный impl-блок, становится яснее для какого трейта мы написали метод get.
А что будет, когда придёт время вызвать эти методы? Чтобы устранить неоднозначность, мы можем использовать полное имя метода (Fully Qualified Syntax).
trait UsernameWidget {
// Получить из виджета имя пользователя
fn get(&self) -> String;
}
trait AgeWidget {
// Получить из виджета возраст
fn get(&self) -> u8;
}
// Форма, реализующая оба трейта: и `UsernameWidget`, и `AgeWidget`
struct Form {
username: String,
age: u8,
}
impl UsernameWidget for Form {
fn get(&self) -> String {
self.username.clone()
}
}
impl AgeWidget for Form {
fn get(&self) -> u8 {
self.age
}
}
fn main() {
let form = Form{
username: "rustacean".to_owned(),
age: 28,
};
// Если вы раскомментируете эту строку, вы получите ошибку, которая говорит
// "multiple `get` found". Потому что это, в конце концов, несколько методов
// с именем `get`.
// println!("{}", form.get());
let username =
assert_eq!("rustacean".to_owned(), username);
let age =
assert_eq!(28, age);
}
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Смотрите также:
Глава "The Rust Programming Language" о полном имени методов (Fully Qualified syntax)
macro_rules!
Rust предоставляет мощную систему макросов, которая позволяет использовать метапрограммирование. Как вы могли видеть в предыдущих главах, макросы выглядят как функции, но их имя заканчивается восклицательным знаком (!). Вместо вызова функции, макросы расширяются в исходный код, который впоследствии компилируется с остальной частью программы. Однако, в отличие от макросов на C и других языках, макросы Rust расширяются в абстрактные синтаксические деревья, а не в подстановку строк, поэтому Вы не получаете неожиданных ошибок приоритета операций.
Макросы создаются с помощью макроса macro_rules!
// Этот простой макрос называется `say_hello`.
macro_rules! say_hello {
// `()` указывает, что макрос не принимает аргументов.
() => (
// Макрос будет раскрываться с содержимым этого блока.
println!("Hello!");
)
}
fn main() {
// Этот вызов будет раскрыт в код `println!("Hello");`
say_hello!()
}
הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
Так почему же макросы полезны?
1. Не повторяйтесь. Есть много случаев, когда вам может понадобиться подобная функциональность в нескольких местах, но с различными типами. Чаще всего написание макроса - это полезный способ избежать повторения кода. (Подробнее об этом позже)
2. Предметно-ориентированные языки. Макросы позволяют определить специальный синтаксис для конкретной цели. (Подробнее об этом позже)
3. Вариативные интерфейсы. Иногда вы хотите объявить интерфейс, принимающий переменное число аргументов. Например, println!, принимающий такое же число аргументов, сколько объявлено в строке с форматом. (Подробнее об этом позже)
Синтаксис
В следующем подразделе мы посмотрим как в Rust объявить макрос. Есть три основные идеи: