Menu
Coddy logo textTech

Реализации по умолчанию

Часть раздела Объектно-ориентированное программирование путешествия по Rust на Coddy. Урок 31 из 61.

До сих пор каждый метод trait, который мы определяли, был всего лишь сигнатурой: требованием без тела. Но что, если большинство типов, реализующих trait, использовали бы одну и ту же логику? Rust позволяет предоставить реализацию по умолчанию непосредственно в определении trait.

Вместо того чтобы заканчивать сигнатуру метода точкой с запятой, добавьте тело:

trait Greet {
    fn greet(&self) -> String {
        String::from("Hello there!")
    }
}

Теперь любой тип, который реализует Greet, автоматически получает этот method, и при этом не нужно писать никакого кода в блоке impl:

struct Guest;

impl Greet for Guest {}

let visitor = Guest;
println!("{}", visitor.greet());  // Привет!

Пустой impl Greet for Guest {} допустим, потому что trait уже предоставляет поведение. struct просто «выбирает использование» trait и бесплатно получает default method.

Этот шаблон полезен, когда вы хотите определить общее поведение, которое работает в большинстве случаев. Типы, которым нужно что-то другое, могут переопределить default (об этом вы узнаете далее), а типам, которых устраивает стандартное поведение, не нужно повторять один и тот же код.

challenge icon

Задание

Легко

Давайте исследуем возможности default implementation, создав систему уведомлений! Ты создашь trait, который сразу предоставляет стандартное поведение, чтобы типы могли подключить его без написания повторяющегося кода.

Ты организуешь код в трёх файлах:

  • notifiable.rs: Define публичный trait Notifiable с method notify(&self) -> String, у которого есть default implementation. По умолчанию он должен возвращать "You have a new notification!". Любой тип, реализующий этот trait, автоматически получит такое поведение без необходимости что-либо писать в своём impl block.
  • alerts.rs: Создай две простые публичные unit struct, которые реализуют твой trait:
    • EmailAlert: реализует Notifiable с пустым impl block (использует default)
    • SystemAlert: также реализует Notifiable с пустым impl block (использует default)
    Обе struct автоматически получат стандартное сообщение уведомления, и тебе не придётся писать тела каких-либо method!
  • main.rs: Объедини свои модули и продемонстрируй, как оба типа alerts используют одинаковое default behavior. Создай экземпляры обеих struct и вызови их method notify.

В своём главном файле создай EmailAlert и SystemAlert, затем выведи их уведомления в отдельных строках.

Твой вывод должен быть таким:

You have a new notification!
You have a new notification!

Обрати внимание, что оба типа создают идентичный вывод: в этом и заключается преимущество default implementations! Ни одной struct не пришлось писать код для method notify, но обе получили полностью работающее поведение уведомлений. На следующем уроке ты узнаешь, как переопределять эти default, когда типу требуется пользовательское поведение.

Попробуйте сами

mod notifiable;
mod alerts;

use notifiable::Notifiable;
use alerts::{EmailAlert, SystemAlert};

fn main() {
    // TODO: Создать экземпляр EmailAlert
    
    // TODO: Создать экземпляр SystemAlert
    
    // TODO: Print the notification from EmailAlert
    
    // TODO: Print the notification from SystemAlert
}
quiz iconПроверьте себя

В этом уроке есть небольшой тест. Начните урок, чтобы ответить на вопросы и сохранить прогресс.

Все уроки раздела Объектно-ориентированное программирование

Потренируйтесь самостоятельно: Онлайн-компилятор Rust