Методы представляют функции, которые определены для структур или перечислений enum. Они позволяют добавить структурам и перечислениям некоторое поведение - некоторые выполняемые действия.
Формальное определение методов:
impl структура/перечисление {
fn название_метода(&self){
// выполняемые действия
}
}
Определение методов заключается в конструкцию, которая определяется с помощью ключевого слова impl. После него идет название структуры или перечисления, для которого определяются методы.
Первый и обязательный параметр метода всегда представляет
ссылку на объект структуры/перечисления, для которого запускается метод. Ссылка на объект структуры/перечисления передается с помощью
&self. Фактически &self представляет параметр self: &Self, где Self представляет ключевое слово, которое
скрывает название текущего типа.
Например, пусть у нас есть некоторая структура Person:
struct Person
{
name: String,
age: u8
}
Добавим к этой структуре метод, который будет выводить ее данные на консоль. Для этого пропишем следующий код:
impl Person{
fn display(&self){
println!("Name: {} Age: {}", &self.name, &self.age);
}
}
После оператора impl указывается название структуры, для которой создается метод - структуры Person.
Метод называется display(). Он имеет только один параметр - ссылку на объект структуры, который будет вызывать данный метод - &self.
В самом методе с помощью ссылки на объект структуры мы можем обратиться к ее данным:
println!("Name: {} Age: {}", &self.name, &self.age);
Вызов метода происходит в следующем формате:
название_объекта.название_метода();
После названия объекта структуры через точку указывается имя метода, после которого в скобках перечисляются значения для параметров метода. (Для параметра &self значение передается автоматически)
Теперь используем выше определенные структуру и метод:
fn main(){
let tom = Person{
name: "Tom".to_string(),
age: 36
};
tom.display();
let bob = Person { name: "Bob".to_string(), age: 41};
bob.display();
}
struct Person
{
name: String,
age: u8
}
impl Person{
fn display(&self){
println!("Name: {} Age: {}", &self.name, &self.age);
}
}
Консольный вывод программы:
Name: Tom Age: 36 Name: Bob Age: 41
В данном случае создается два объекта структуры Person: tom и bob. При вызове
tom.display();
В качестве ссылки на текущий объект &self передается ссылка на объект tom, поскольку этот объект вызывает данный метод.
При этом при вызове метода для параметра &self не надо передавать никаких значений. Поэтому при вызове метода мы не передаем ему никаких параметров.
Аналогично при вызове
bob.display();
В метод будет передаваться ссылка на объект bob, поэтому этот вызов метода выведет на консоль данные для объекта bob.
Стоит отметить, что в принципе мы здесь используем еще один метод - метод to_string(), который преобразует значение к типу String:
name: "Tom".to_string(),
Также стоит отметить, что необязательно передавать текущий обект в метод имеено через ссылку. Так, мы могли бы передвать его напрямую, не по ссылке:
fn main(){
let tom = Person{
name: "Tom".to_string(),
age: 36
};
tom.display(); // передача владения tom в функцию display
tom.display(); // ! Здесь ошибка
}
struct Person
{
name: String,
age: u8
}
impl Person{
fn display(self){ // данные передаются не по ссылке, а напрямую
println!("Name: {} Age: {}", self.name, self.age);
}
}
Однако в этом случае, как и общем случае, будет происходить передача владения. Соответственно после одного вызова метода display мы больше не сможем использовать объект Person,
поэтому обычно сигнатура методов содержит именно ссылку &self, а не просто self.
Как и любая функция метод может принимать и другие параметры Например, добавим метод, с помощью которого один объект Person будет приветствовать другой объект Person:
fn main(){
let tom = Person { name: "Tom".to_string(), age: 36};
let bob = Person { name: "Bob".to_string(), age: 41};
tom.say_hello(&bob, 12);
bob.say_hello(&tom, 19);
}
struct Person { name: String, age: u8 }
impl Person{
fn say_hello(&self, other: &Person, hour: u8){
if hour < 16{
println!("Добрый день, {}!", other.name);
}
else {
println!("Добрый вечер, {}!", other.name);
}
}
fn display(&self){
println!("Name: {} Age: {}", self.name, self.age);
}
}
Итак, здесь добавлен метод say_hello(), который принимает три параметра. Первый параметр традиционно - ссылка на текущий объект. Второй
параметр - other представляет ссылку на структуру Person - человека, которого надо приветствовать. И третий параметр - hour - текущий час. В самом методе в зависимости
от текущего часа выводим на консоль то или иное приветствие.
Поскольку метод принимает три параметра, то при вызове метода в него надо передать два значения - для второго и третьего параметра (как уже говорилось выше, для первого параметра передавать никаких значений не надо):
tom.say_hello(&bob, 12);
Консольный вывод программы:
Добрый день, Bob! Добрый вечер, Tom!
Также, как и обычные функции, метод может возвращать некоторое значение. Например, определим метод, который будет сравнивать два объекта Person по возрасту - кто старше и в зависимости от проверки возвращать некоторый результат:
fn main(){
let tom = Person { name: "Tom".to_string(), age: 36};
let bob = Person { name: "Bob".to_string(), age: 41};
let is_older = tom.is_older(&bob);
if is_older{
println!("{} старше чем {}", tom.name, bob.name);
}
else{
println!("{} младше чем {} или одного возраста с ним", tom.name, bob.name);
}
}
struct Person { name: String, age: u8 }
impl Person{
fn is_older(&self, other: &Person) -> bool{
self.age > other.age
}
}
Метод сравнения на возраст называется is_older(). В качестве параметра он принимает объект Person, с которым проводится сравнение. В качестве результата
метод возвращает значение типа bool: true, если возраст текущего объекта Person больше возраста объекта other, либо
false, если возраст меньше или равен.
При вызове в метод передается значение типа Person:
let is_older = tom.is_older(&bob);
Полученный из метода результат сохраняется в переменную is_older. Поскольку этот результат представляет тип bool, то мы можем
использовать в конструкции if..else и в зависимости от его значения вывести на консоль то или иное значение.
В данном случае консольный вывод будет следующим:
Tom младше чем Bob или одного возраста с ним
Если необходимо изменять текущий объект в методе, то первый параметр метода определяется как изменяемая ссылка - &mut self. Например:
fn main(){
let mut tom = Person { name: "Tom".to_string(), age: 36};
println!("До изменения: {}", tom.age); // 36
tom.change_age(22);
println!("После изменения: {}", tom.age); // 22
}
struct Person { name: String, age: u8 }
impl Person{
fn change_age(&mut self, age: u8){
self.age = age;
}
}
В данном случае определен метод change_age(), который изменяет возраст текущего объекта Person с помощью второго параметра. Соответственно первый
параметр определен как &mut self. Кроме того, переменная, которая представляет текщий объект определена с mut:
let mut tom = Person { name: "Tom".to_string(), age: 36};