Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Стоит ли определять трейт-расширение?

В каких сценариях следует предпочесть трейт-расширение свободной функции?

// Copyright 2025 Google LLC
// SPDX-License-Identifier: Apache-2.0

pub trait StrExt {
    fn is_palindrome(&self) -> bool;
}

impl StrExt for &str {
    fn is_palindrome(&self) -> bool {
        self.chars().eq(self.chars().rev())
    }
}

// против

fn is_palindrome(s: &str) -> bool {
    s.chars().eq(s.chars().rev())
}

Главное преимущество трейтов-расширений — простота обнаружения.

  • Обнаруживаемость: методы-расширения легче найти, чем свободные функции. Языковые серверы (например, rust-analyzer) предложат их, если вы введёте . после экземпляра внешнего типа.

  • Цепочки вызовов: важное эргономическое преимущество трейтов-расширений — цепочки вызовов методов. Именно на этом основан трейт Iterator, который позволяет писать плавные вызовы вроде data.iter().filter(...).map(...). Добиться того же с помощью свободных функций было бы гораздо неудобнее (map(filter(iter(data), ...), ...)).

  • Обобщения и dyn: трейт можно использовать как ограничение трейта в обобщённом коде или как часть dyn Trait, тогда как свободная функция не всегда может использоваться в обобщённом контексте.

  • Связность API: трейты-расширения помогают создать связный API. Если у вас есть несколько связанных функций для внешнего типа (например, is_palindrome, word_count, to_kebab_case), объединить их в один трейт StrExt часто удобнее, чем предоставлять пользователю несколько свободных функций для импорта.

  • Компромиссы: несмотря на эти преимущества, специально написанный трейт-расширение может оказаться избыточным для одной простой функции. Оба подхода требуют дополнительного импорта, а привычный синтаксис методов может не оправдывать шаблонного кода полного определения трейта.