История вопроса
Rust заимствует концепцию методов у объектно-ориентированных языков, но реализует их иначе: вместо привычного this или self методы явно принимают self-параметр. Ассоциированные функции же появились как альтернатива статическим методам в других ЯП — они связаны с типом, но не с конкретным значением.
Проблема
Часто путают методы, ассоциированные функции и свободные функции. Неочевидно, когда использовать метод с self, а когда ассоциированную функцию без self. Есть вопросы видимости, автодереференса, передачи владения.
Решение
Методы в Rust объявляют с первым параметром self/ &self/ &mut self внутри impl блока (обычно для struct или enum). Они вызываются на экземпляре: object.method(). Ассоциированные функции (например, new, from) объявляются также внутри impl, но без первого параметра self и вызываются через двойное двоеточие: Type::function().
Пример кода:
struct Point { x: f64, y: f64, } impl Point { // Ассоциированная функция (конструктор) fn new(x: f64, y: f64) -> Self { Self { x, y } } // Метод: требует self fn distance_from_origin(&self) -> f64 { (self.x.powi(2) + self.y.powi(2)).sqrt() } } let p = Point::new(3.0, 4.0); printf!("{}", p.distance_from_origin()); // 5.0
Ключевые особенности:
::Можно ли вызывать ассоциированные функции через экземпляр (через точку)?
Такое возможно (например, p.new(1.0, 2.0)), но крайне не рекомендуется: это вводит в заблуждение, потому что ассоциированная функция не имеет доступа к текущему объекту, а экземпляр передаётся игнорируясь. Лучше использовать синтаксис Type::func().
Пример кода:
let p = Point::new(1.0, 2.0); let q = p.new(0.0, 0.0); // Работает, но не best practice!
Могут ли методы быть асинхронными?
Да. Методы могут объявляться с ключевым словом async так же, как и свободные функции:
impl Foo { async fn do_async(&self) { // ... } }
Можно ли внутри одного impl блока объявить и методы, и ассоциированные функции?
Да — любые комбинации допустимы. Также возможно объявить несколько impl блоков для одного типа.
Новичок объявил функцию new вне impl и пытался использовать её как конструктор, а потом случайно вызвал через экземпляр: p.new(1.0, 2.0).
Плюсы:
Быстро работает (компилятор позволяет).
Минусы:
Код нечитаем, сложно поддерживать, сложно использовать методы с правильным владением self.
Все методы и ассоциированные функции объявлены строго внутри impl, используются правильные синтаксисы вызова (Type::new() для конструкторов, obj.method() для действий).
Плюсы:
Высокая читаемость, соответствие best practices.
Минусы:
Требует знания идиом Rust и внимательности к синтаксису.