Rust для JavaScript-разработчиков - функции и поток управления
Перевод | Автор оригинала: Shesh
Это третья часть из серии о знакомстве с языком Rust для JavaScript-разработчиков. Вот все главы:
- Обзор инструментальной экосистемы
- Переменные и типы данных
- Функции и поток управления
- Сопоставление с образцом и перечисления
Функции
Синтаксис функций в Rust очень похож на синтаксис JavaScript.
fn main() {
  let income = 100;
  let tax = calculate_tax(income);
  println!("{}", tax);
}
fn calculate_tax(income: i32) -> i32 {
  return income * 90 / 100;
}
Единственное различие, которое вы можете увидеть выше, - это аннотации типов для аргументов и возвращаемых значений.
Ключевое слово return можно пропустить, и очень часто можно увидеть код без явного возврата. Если вы возвращаете неявно, не забудьте удалить точку с запятой из этой строки. Вышеупомянутую функцию можно реорганизовать как:
fn main() {
  let income = 100;
  let tax = calculate_tax(income);
  println!("{}", tax);
}
fn calculate_tax(income: i32) -> i32 {
- return income * 90 / 100;
+ income * 90 / 100
}
Стрелочные функции
Стрелочные функции - популярная функция в современном JavaScript - они позволяют писать функциональный код в сжатой форме.
В Rust есть нечто подобное, и они называются «замыканиями». Название может немного сбивать с толку, и к нему нужно привыкнуть, потому что в JavaScript замыкания могут быть созданы с использованием как обычных, так и стрелочных функций.
Синтаксис замыкания Rust очень похож на стрелочные функции JavaScript:
Без аргументов:
// JavaScript
let greet =() => console.log("hello");
greet(); // "hello"
// Rust
let greet = || println!("hello");
greet(); // "hello"
С аргументами:
// JavaScript
let greet = (msg) => console.log(msg);
greet("good morning!"); // "good morning!"
// Rust
let greet = |msg: &str| println!("{}", msg);
greet("good morning!"); // "good morning!"
Возвращаемые значения:
// JavaScript
let add = (a, b) => a + b;
add(1, 2); // 3
// Rust
let add = |a: i32, b: i32| -> i32 { a + b };
add(1, 2); // 3
Многострочный:
// JavaScript
let add = (a, b) => {
  let sum = a + b;
  return sum;
};
add(1, 2); // 3
// Rust
let add = |a: i32, b: i32| -> i32 {
  let sum = a + b;
  return sum;
};
add(1, 2); // 3
Вот шпаргалка:

Замыкания в большинстве случаев не нуждаются в аннотациях типов, но я добавил их сюда для ясности.
If Else
fn main() {
  let income = 100;
  let tax = calculate_tax(income);
  println!("{}", tax);
}
fn calculate_tax(income: i32) -> i32 {
  if income < 10 {
    return 0;
  } else if income >= 10 && income < 50 {
    return 20;
  } else {
    return 50;
  }
}
Loops
While loops:
fn main() {
  let mut count = 0;
  while count < 10 {
    println!("{}", count);
    count += 1;
  }
}
Обычных циклов for в Rust не существует, нам нужно использовать циклы while или for..in. Циклы for..in похожи на циклы for..of в JavaScript, и они перебирают итератор.
fn main() {
  let numbers = [1, 2, 3, 4, 5];
  for n in numbers.iter() {
    println!("{}", n);
  }
}
Обратите внимание, что мы не выполняем итерацию непосредственно по массиву, а вместо этого используем метод iter массива.
Мы также можем перебирать диапазоны:
fn main() {
  for n in 1..5 {
    println!("{}", n);
  }
}
Итераторы
В JavaScript мы можем использовать методы массива, такие как map / filter / reduce / etc, вместо циклов for для выполнения вычислений или преобразований в массиве.
Например, здесь мы берем массив чисел, удваиваем их и отфильтровываем элементы меньше 10:
function main() {
  let numbers = [1, 2, 3, 4, 5];
  let double = (n) => n * 2;
  let less_than_ten = (n) => n < 10;
  let result = numbers.map(double).filter(less_than_ten);
  console.log(result); // [2, 4, 6, 8]
}
В Rust мы не можем напрямую использовать map / filter / etc для векторов, нам нужно выполнить следующие шаги:
- Преобразуйте вектор в итератор с помощью методов iter, into_iter или iter_mut.
- Цепочка адаптеров, таких как map / filter / и т.д. На итераторе.
- Наконец преобразуйте итератор обратно в вектор, используя такие потребители, как сбор, поиск, суммирование и т.д.
Вот эквивалентный код на Rust:
fn main() {
  let numbers = vec![1, 2, 3, 4, 5];
  let double = |n: &i32| -> i32 { n * 2 };
  let less_than_10 = |n: &i32| -> bool { *n < 10 };
  let result: Vec<i32> = numbers.iter().map(double).filter(less_than_10).collect();
  println!("{:?}", result); // [2, 4, 6, 8]
}
Вы должны понимать большую часть приведенного выше кода, но можете заметить здесь несколько вещей:
- Использование & и * в закрытии
- Аннотация типа Vecдля переменной результата 
& - это оператор ссылки, а * - оператор разыменования. Метод iter вместо того, чтобы копировать элементы в векторе, передает их как ссылки на следующий адаптер в цепочке. Вот почему мы используем & i32 в закрытии карты (двойное). Это закрытие возвращает i32, но фильтр вызывает его закрытие (less_than_10) со ссылкой, поэтому нам нужно снова использовать & i32. Чтобы разыменовать аргумент, мы используем оператор *. Мы рассмотрим это более подробно в следующих главах.
Что касается Vec
Помимо карты и фильтра, есть масса других полезных адаптеров, которые мы можем использовать в итераторах.
Спасибо за чтение! Не стесняйтесь подписываться на меня в Твиттере, чтобы увидеть больше подобных сообщений :)
 Dudochkin Victor
        Dudochkin Victor