Rust для JavaScript-разработчиков - переменные и типы данных
Перевод | Автор оригинала: Shesh
Это вторая часть из серии о знакомстве с языком Rust для JavaScript-разработчиков. Вот все главы:
- Обзор инструментальной экосистемы
- Переменные и типы данных
- Функции и поток управления
- Сопоставление с образцом и перечисления
Переменные
В JavaScript есть три способа объявления переменных - var, const и let. В Rust также есть const и let, но они работают иначе, чем в JavaScript.
let
В Rust мы объявляем переменные с помощью let.
let a = 123;
Как и ожидалось, переменной a присваивается значение 123. Но Rust по умолчанию делает переменную неизменной. Вы не сможете изменить значение после того, как оно будет присвоено.
let a = 123;
a = 456; // Error! :(
На первый взгляд, это может быть похоже на const в JavaScript, но константа в JavaScript не делает переменные неизменяемыми, а просто делает привязку неизменной.
Если вы хотите сделать переменную изменяемой, вам нужно явно указать ее с помощью ключевого слова mut
let mut a = 123;
a = 456; // No Error :)
const
Const в Rust также сильно отличается от const в JavaScript - лучше думать о const в Rust как о «метке» для постоянного значения. Во время компиляции они заменяются своими фактическими значениями во всех местах, где они используются. Обычно он используется для констант, таких как номера портов, значения тайм-аута, коды ошибок и т.д.
Вы также можете определять константные внешние функции на глобальном уровне, что нельзя сделать с помощью let.
Типы данных
Числа
В JavaScript тип Number используется как для целых (числа без десятичной точки), так и для чисел с плавающей запятой (числа с десятичной точкой). В Rust есть множество опций для целых чисел и чисел с плавающей запятой, но по умолчанию мы можем использовать i32 для целых чисел и f64 для чисел с плавающей запятой.
let x = 123; // i32
let y = 4.5; // f64
Булевы
Довольно просто - и JavaScript, и Rust имеют логические значения со значениями true / false.
let x = false; // bool
Строки
Обычно мы мало думаем о строках при работе с JavaScript - они «просто работают». В Rust существует множество типов строк, но давайте остановимся на наиболее широко используемых - String и &str.
Строка может увеличиваться, тогда как &str - неизменяемый и фиксированный размер.
Когда вы создаете строку с помощью строкового литерала, она создает тип &str:
let name = "Saitama"; // &str
Вам нужно использовать методы String::from или to_string для создания типа String:
let name = String::from("Genos"); // String
let name2 = "King".to_string(); // String
Вы можете преобразовать из String в &str с помощью функции as_str
let name2 = "King".to_string(); // String
let name3 = name2.as_str(); // &str
Мы узнаем больше о строках в будущих публикациях.
Опции
В JavaScript есть два типа пустых значений - undefined и null. Undefined используется, когда переменная, свойство и т.д. Не определены, а null используется, когда что-то намеренно пусто.
В Rust нет ни того, ни другого - у него даже нет специального нулевого типа данных. Вместо этого у него есть что-то, называемое Option. Когда у нас есть ситуация, когда значение может быть пустым или изначально неопределенным, используется этот тип Option.
Люди, которые работали с TypeScript / Flow, могут увидеть здесь некоторое сходство, но это совсем другое с точки зрения создания и использования дополнительных опций.
Допустим, мы хотим написать функцию, которая принимает путь к файлу и возвращает его содержимое. Допустим, по какой-то причине мы хотим вернуть «нулевое» значение, если в качестве пути к файлу передается пустая строка.
Вот как это можно было бы написать на JavaScript / TypeScript:
function read_file(path: string): string | null {
const contents = "hello";
if (path !== "") {
return contents;
}
return null;
}
Реализация того же самого с помощью Rust's Option:
fn read_file(path: &str) -> Option<&str> {
let contents = "hello";
if path != "" {
return Some(contents);
}
return None;
}
Вы можете видеть, что мы возвращаем None для нулевого значения, но для ненулевого значения мы не возвращаем содержимое в том виде, в каком оно есть, а скорее «оборачиваем» его в Some и возвращаем его. Тип возвращаемого значения также не является «строковым или нулевым» в соответствии с примером TypeScript, а является типом «Option, который содержит &str».
Вот как бы вы могли вызвать эту функцию в JavaScript / TypeScript:
function main() {
const file_contents = read_file("/path/to/file");
if (file_contents !== null) {
console.log(file_contents); // file_contents is refined to string type
} else {
console.log("Empty!");
}
}
Вызов функции в Rust:
fn main() {
let file = read_file("path/to/file");
if file.is_some() {
let contents = file.unwrap();
println!("{}", contents);
} else {
println!("Empty!");
}
}
Как видите, нам нужно вручную «развернуть» Option, чтобы получить содержимое внутри.
Массивы
Подобно строкам, существует два типа массивов - один с фиксированным размером (называемый просто «массивом»), а другой, который может увеличиваться / уменьшаться в размере (называемый «векторами»).
Массивы:
fn main() {
let list = [1, 2, 3];
println!("{:?}", list);
}
Векторы:
fn main() {
let mut list = vec![1, 2, 3];
list.push(4);
println!("{:?}", list);
}
Объекты
Технически все непримитивные типы являются «объектами» в JavaScript, но мы обычно используем термин «объект» для двух вещей - пакета данных или хеш-карты.
Пакет данных:
В отличие от других языков, вам не нужно проходить много церемоний для создания объекта, и это одна из самых крутых особенностей JavaScript.
Чтобы создать объект сотрудника в JavaScript:
function main() {
const employee = {
name: "Saitama",
age: 25,
occupation: "Hero",
};
}
Чтобы создать такой же объект в Rust, мы можем использовать структуры:
struct Employee {
name: String,
age: i32,
occupation: String,
}
fn main() {
let employee = Employee {
name: "Saitama".to_string(),
age: 25,
occupation: "Hero".to_string(),
};
}
HashMap:
В JavaScript для создания объекта с произвольными парами ключ-значение мы можем использовать либо обычные литералы объекта, либо объект Map:
function main() {
const colors = new Map();
colors.set("white", "#fff");
colors.set("black", "#000");
console.log(colors.get("white")); // #fff
}
В Rust вы можете сделать то же самое, используя тип HashMap:
use std::collections::HashMap;
fn main() {
let mut colors = HashMap::new();
colors.insert("white".to_string(), "#fff");
colors.insert("black".to_string(), "#000");
println!("{:?}", colors.get("white").unwrap()); // #fff
}
Обратите внимание на использование развёртки выше. Метод get HashMap возвращает тип Option, который нам нужно развернуть, чтобы получить значение внутри.
Спасибо за чтение! Не стесняйтесь подписываться на меня в Twitter, чтобы увидеть больше подобных сообщений :)