Тип Result

Последнее обновление: 04.05.2021

Многие ошибки не требуют завершения программы. Такие ошибки можно обработать и продолжить выполнение программы. Для этой цели в Rust предназначен тип Result. Данный тип представляет enum или перечисление, которое определяет две константы: Ok и Err.

enum Result<T, E> {
    Ok(T),
    Err(E),
}

Это перечисление типизировано двумя параметрами типа: T и E. T представляет тип значения, который будет возвращаться в случае успешного выполнения. А параметр E представляет тип ошибки, которая будет возвращаться через константу Err в случае возникновения ошибки.

Рассмотрим, как использовать перечисление Result. Допустим, у нас есть функция, которая создает объект некоторой структуры:

struct Person{ name: String, age:u8}

fn create_person(username: &str, userage: u8) -> Result<Person, String>{
	
	if userage < 110{
		let new_person = Person{name: String::from(username), age: userage };
		Result::Ok(new_person)
	}
	else { 
		Result::Err(String::from("Некорректный возраст. Возраст должен быть меньше 110")) 
	}
}

Итак, у нас есть структура Person, которая содержит два поля: name - имя пользователя и age - возраст пользователя. И также определена функция create_person(), которая принимает данные для имени и возраста пользователя и по ним создает объект Person. Однако данная функция возвращает не просто объект Person, а объект перечисления Result<Person, String>. А это значит, что функция должна возвращать либо константу Ok, в которую передается значение Person, либо константу Err, в которую должно передаваться значение String.

Логика функции проста: если возраст корректен (в данном случае меньше 110), то объект Person создается. Если возраст некорретен, то нет смысла создавать объект Person.

Если действия завершились успешно, то возвращается константа Ok, которая содержит созданный объект Person.

Result::Ok(new_person)

Если действия по созданию объекта Person завершились неудачно, то возвращается константа Err, которая содержит информацию об ошибке в виде объекта String - некоторое сообщение об ошибке.

Result::Err(String::from("Некорректный возраст. Возраст должен быть меньше 110"))

Теперь применим это в программе:

fn main() {
	// пример корректного ввода - функция возвращает константу Result::Ok
	let tom_result = create_person("Tom", 36);
	match tom_result{
		Ok(tom) => println!("Name: {}  Age: {}", tom.name, tom.age),
		Err(err_message) => println!("{}", err_message)
	}
	
	// пример некорректного ввода - функция возвращает константу Result::Err
	let bob_result = create_person("Bob", 136);
	match bob_result{
		Ok(bob) => println!("Name: {}  Age: {}", bob.name, bob.age),
		Err(err_message) => println!("{}", err_message)
	}
	
    println!("Конец программы...");
}

struct Person{ name: String, age:u8}

fn create_person(username: &str, userage: u8) -> Result<Person, String>{
	
	if userage < 110{
		let new_person = Person{name: String::from(username), age: userage };
		Result::Ok(new_person)
	}
	else { 
		Result::Err(String::from("Некорректный возраст. Возраст должен быть меньше 110")) 
	}
}

Вначале передаем корректные данные:

let tom_result = create_person("Tom", 36);

Чтобы узнать, что собой представляет полученный результат tom_result, используем конструкцию match:

match tom_result{
	Ok(tom) => println!("Name: {}  Age: {}", tom.name, tom.age),
	Err(err_message) => println!("{}", err_message)
}

Если создание объекта Person завершилось успешно, то мы получим константу Ok. В данном случае из константы в переменную tom получаем возвращенный объект Person и выводим его данные на консоль:

Ok(tom) => println!("Name: {}  Age: {}", tom.name, tom.age),

Если создание Person завершилось неудачно, то выполняется выражение

Err(err_message) => println!("{}", err_message)

которое помещает в переменную err_message объект String с сообщением об ошибке и выводит его на консоль.

Для второго вызова функции логика аналогична. В итоге мы получим следующий консольный вывод:

Name: Tom  Age:36
Некорректный возраст. Возраст должен быть меньше 110
Конец программы...

Вызов макроса panic

Как именно обрабатывать ошибку, зависит от разных условий. Например, в примере выше предполагалось, что отсутствие объекта Person некритично. Однако вероятны ситуации, когда ошибка может повлиять на возможность дальнейшего выполнения программы. Вполне возможно, что если объект Person не создан, то дальше нет смысла продолжать работу программы. И в этом случае ее можно завершить макросом panic!:

fn main() {

	let tom_result = create_person("Tom", 36);
	let tom = match tom_result{
		Ok(person) => person,
		Err(err_message) => panic!("Возникла проблема: {}", err_message)
	};
	
	println!("Name: {}  Age: {}", tom.name, tom.age);
}

Здесь, используя синтаксис паттернов, получаем из результата tom_result значение Person. Если результат представляет константу Ok(), то передаем из нее в переменную person объект Person и возвращаем его:

Ok(person) => person

В итоге это значение перейдет в переменную tom. И далее мы сможем, например, вывести полученные данные на консоль или как-то иным образом использовать полученный объект Person.

Если результат представляет константу Err, то вызываем макрос panic!, в котором выводит сообщение на консоль. Соответственно при выполнении этого макроса программа завершает свое выполнение.

Пример выполнения макроса panic при вводе некорректных данных:

enum Result and OK and Err in Rust
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850