Это вторая часть из цикла статей по переводу книги «Rust by Example». Первую часть можно прочитать здесь.
Продолжим?
Содержание
1. Выражения
В Rust почти все фигурные скобки являются выражением, это значит, что они могут вернуть какой-то результат. Такое поведение не всегда нужно, чтобы ничего не возвращать добавьте ; в конец.
Выражения в блоке могут использоваться в качестве r-values значений, а последнее будет назначено как l-value.
* Что такое Rvalue и Lvalue читайте здесь.
Но, если последнее выражение в блоке будет точкой с запятой, результат будет равен пустому кортежу: ().
fn main() {
let x = 5u;
let y = {
let x_squared = x * x;
let x_cube = x_squared * x;
// `y` будет этим выражением
x_cube + x_squared + x
};
let z = {
// Если в конце стоит точка с запятой, то выражение не
// присваивается, вместо него переменная `z` будет содержать `()`
2 * x;
};
println!("x is {}", x);
println!("y is {}", y);
println!("z is {}", z);
}
2. Операторы ветвления
Ветвление if-else
является C-подобным. В отличие от C, логическое условие не должно быть заключено в круглые скобки, а каждому условию необходимы фигурные скобки.
if-else
так же является выражением; и, из-за типобезопасности Rust, все ветви должны возвращать значение одного типа.
fn main() {
let n = 5i;
if n < 0 {
print!("{} is negative", n);
} else if n > 0 {
print!("{} is positive", n);
} else {
print!("{} is zero", n);
}
let big_n =
if n < 10 && n > -10 {
println!(", and is a small number, increase ten-fold");
// Это выражение возвращает `int`
10 * n
} else {
println!(", and is a big number, reduce by two");
// Это выражение должно возвращать `int`
n / 2
// ^ Попробуйте поставить точку с запятой
};
println!("{} -> {}", n, big_n);
}
3. Петли
Ключевое слово loop
в Rust создает бесконечный цикл (петлю).
Оператор break
позволяет выйти из петли в любое время, а continue
пропускает оставшуюся часть итерации и начинает выполнение заново.
fn main() {
let mut count = 0u;
println!("Let's count until infinity!");
// Бесконечная петля
loop {
count += 1;
if count == 3 {
println!("three");
// Пропустим оставшуюся часть этой итерации
continue;
}
println!("{}", count);
if count == 5 {
println!("OK, that's enough");
// Выход из этой петли
break;
}
}
}
3.1 Вложенность и ярлыки
Петли можно делать вложенными. В таких случаях они должны содержать какой-то ярлык 'label
, а операторы break/continue
писаться с этим ярлыком.
fn main() {
'outer: loop {
println!("Entered the outer loop");
'inner: loop {
println!("Entered the inner loop");
// Выход из внутренней петли
//break;
// Выход из внешней петли
break 'outer;
}
println!("This point will never be reached");
}
println!("Exited the outer loop");
}
4. Цикл while
Тело цикла будет выполняться пока условие в while
не будет истинно.
Давайте напишем fizzbuzz используя цикл while
.
fn main() {
// Переменная для счетчика
let mut n = 1u;
// Итерируем пока `n` меньше 101
while n < 101 {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
// Увеличиваем счетчик
n += 1;
}
}
5. Цикл for и функция range
Конструкцию for in
можно использовать для перебора Iterator
и генератора ленивых значений (подробнее позже). Функция range
это один из наиболее распространенных итераторов. range(a, b)
будет выдавать значения от a до b-1 изменяя шаг на единицу.
Давайте напишем fizzbuzz используя for
вместо while
.
fn main() {
// `n` будет принимать значения: 1, 2, ..., 100 в каждой итерации
for n in range(1u, 101) {
if n % 15 == 0 {
println!("fizzbuzz");
} else if n % 3 == 0 {
println!("fizz");
} else if n % 5 == 0 {
println!("buzz");
} else {
println!("{}", n);
}
}
}
6. Функции
Функции объявляются при помощи ключевого слова fn. Аргументы аннотируются типами, как и переменные; и, если функция возвращает значение, возвращаемый тип должен быть указан после стрелки ->.
Последнее выражение в теле функции будет использовано как возвращаемое значение или можно использовать оператор return
для раннего возврата значения из функции, даже из внутреннего цикла или условного оператора.
Перепишем fizzbuzz используя функции!
// Функция, которая возвращает булево значение
fn is_divisible_by(lhs: uint, rhs: uint) -> bool {
// Некорректный входной параметр, сразу выходим из функции
if rhs == 0 {
return false;
}
// Это выражение, так что ключевое слово `return` тут не нужно
lhs % rhs == 0
}
// Функции, которые не возвращают значение, на самом деле возвращают тип `()`
fn fizzbuzz(n: uint) -> () {
if is_divisible_by(n, 15) {
println!("fizzbuzz");
} else if is_divisible_by(n, 3) {
println!("fizz");
} else if is_divisible_by(n, 5) {
println!("buzz");
} else {
println!("{}", n);
}
}
// Если функция возвращает `()`, возвращаемый тип можно опустить
fn fizzbuzz_to(n: uint) {
for n in range(1, n + 1) {
fizzbuzz(n);
}
}
fn main() {
fizzbuzz_to(100);
}
6.1 Неиспользуемые функции
Компилятор обеспечивает dead_code
lint, чтобы предупреждать о неиспользованных функциях. Можно добавить атрибут #[allow(dead_code)]
, чтобы отключить уведомление.
fn used_function() {}
// `#[allow(dead_code)]` это атрибут, который отключает предупреждение `dead_code`
#[allow(dead_code)]
fn unused_function() {}
fn noisy_unused_function() {}
// ИСПРАВЬТЕ ^ Добавьте атрибут, чтобы отключить предупреждение
fn main() {
used_function();
}
Обратите внимание, что в реальных программах, вы должны устранить «мертвый код». В этих примерах мы его используем для демонстрации.
Заключение
Присоединяйтесь к google-группе: Rust по-русски для получения дополнительной информации по этому языку.
Можно помочь с переводом на Github: github.com/eg0r/rust-by-example
Все замечания, ошибки или неточности отправляйте мне в почту.
Автор: seweb