Синтаксис методов
Функции — это хорошо, но если вы хотите вызвать несколько связных функций для каких-либо данных, то это может быть неудобно. Рассмотрим этот код:
baz(bar(foo)));
Читать данную строку кода следует слева направо, поэтому мы наблюдаем такой порядок: «baz bar foo». Но он противоположен порядку, в котором функции будут вызываться: «foo bar baz». Было бы классно записать вызовы в том порядке, в котором они происходят, не так ли?
foo.bar().baz();
К счастью, как вы уже наверно догадались, это возможно! Rust предоставляет
возможность использовать такой синтаксис вызова метода с помощью ключевого
слова impl.
Вызов методов
Вот как это работает:
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}
impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}
fn main() {
    let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
    println!("{}", c.area());
}
Этот код напечатает 12.566371.
Мы создали структуру, которая представляет собой круг. Затем мы написали блок
impl и определили метод area внутри него.
Методы принимают специальный первый параметр, &self. Есть три возможных
варианта: self, &self и &mut self. Вы можете думать об этом специальном
параметре как о x в x.foo(). Три варианта соответствуют трем возможным видам
элемента x: self — если это просто значение в стеке, &self — если это
ссылка и &mut self — если это изменяемая ссылка. Мы передаем параметр &self
в метод area, поэтому мы можем использовать его так же, как и любой другой
параметр. Так как мы знаем, что это Circle, мы можем получить доступ к полю
radius так же, как если бы это была любая другая структура.
По умолчанию следует использовать &self, также как следует предпочитать
заимствование владению, а неизменные ссылки изменяемым. Вот пример, включающий
все три варианта:
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}
impl Circle {
    fn reference(&self) {
       println!("принимаем self по ссылке!");
    }
    fn mutable_reference(&mut self) {
       println!("принимаем self по изменяемой ссылке!");
    }
    fn takes_ownership(self) {
       println!("принимаем владение self!");
    }
}
Цепочка вызовов методов
Итак, теперь мы знаем, как вызвать метод, например foo.bar(). Но что насчет
нашего первоначального примера, foo.bar().baz()? Это называется «цепочка
вызовов», и мы можем сделать это, вернув self.
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}
impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
    fn grow(&self, increment: f64) -> Circle {
        Circle { x: self.x, y: self.y, radius: self.radius + increment }
    }
}
fn main() {
    let c = Circle { x: 0.0, y: 0.0, radius: 2.0 };
    println!("{}", c.area());
    let d = c.grow(2.0).area();
    println!("{}", d);
}
Проверьте тип возвращаемого значения:
# struct Circle;
# impl Circle {
fn grow(&self) -> Circle {
# Circle } }
Мы просто указываем, что возвращается Circle. С помощью этого метода мы можем
создать новый круг, площадь которого будет в 100 раз больше, чем у старого.
Статические методы
Вы также можете определить методы, которые не принимают параметр self. Вот
шаблон программирования, который очень распространен в коде на Rust:
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}
impl Circle {
    fn new(x: f64, y: f64, radius: f64) -> Circle {
        Circle {
            x: x,
            y: y,
            radius: radius,
        }
    }
}
fn main() {
    let c = Circle::new(0.0, 0.0, 2.0);
}
Этот статический метод, который создает новый Circle. Обратите внимание, что
статические методы вызываются с помощью синтаксиса: Struct::method(), а не
ref.method().
Шаблон «строитель» (Builder Pattern)
Давайте предположим, что нам нужно, чтобы наши пользователи могли создавать
круги и чтобы у них была возможность задавать только те свойства, которые им
нужны. В противном случае, атрибуты x и y будут 0.0, а radius будет
1.0. Rust не поддерживает перегрузку методов, именованные аргументы или
переменное количество аргументов. Вместо этого мы используем шаблон «строитель».
Он выглядит следующим образом:
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}
impl Circle {
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }
}
struct CircleBuilder {
    x: f64,
    y: f64,
    radius: f64,
}
impl CircleBuilder {
    fn new() -> CircleBuilder {
        CircleBuilder { x: 0.0, y: 0.0, radius: 0.0, }
    }
    fn x(&mut self, coordinate: f64) -> &mut CircleBuilder {
        self.x = coordinate;
        self
    }
    fn y(&mut self, coordinate: f64) -> &mut CircleBuilder {
        self.y = coordinate;
        self
    }
    fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
        self.radius = radius;
        self
    }
    fn finalize(&self) -> Circle {
        Circle { x: self.x, y: self.y, radius: self.radius }
    }
}
fn main() {
    let c = CircleBuilder::new()
                .x(1.0)
                .y(2.0)
                .radius(2.0)
                .finalize();
    println!("площадь: {}", c.area());
    println!("x: {}", c.x);
    println!("y: {}", c.y);
}
Всё, что мы сделали здесь — это создали ещё одну структуру, CircleBuilder. В
ней мы определили методы строителя. Также мы определили метод area() в
Circle. Мы также сделали еще один метод в CircleBuilder: finalize(). Этот
метод создаёт наш окончательный Circle из строителя. Таким образом, мы можем
использовать методы CircleBuilder чтобы уточнить создание Circle.