На момент написания этого топика автор книги создал 49 глав, в первой части будет перевод первых пяти. Убедитесь, что Rust установлен и под рукой имеется документация.
Содержание
Примечание:
- При написании программ использовался компилятор версии Nightly (0.10), не забудьте про это.
- Запустить код можно в Rust Playpen: http://play.rust-lang.org
1. Привет, мир!
Это код традиционной программы «Hello World»:
// Комментарии игнорируются компилятором // Это основная функция fn main() { // Вывести текст в консоль println!("Hello World!"); }
println!
это макрос (мы рассмотрим их позже), который печатает текст в консоль.
Программа может быть сгенерирована с помощью компилятора Rust rustc:
$ rustc hello.rs
rustc создаст бинарный файл «hello», который можно запустить:
$ ./hello Hello World!
2. Форматированный вывод
Макрос println!
не только выводит в консоль, а также способен форматировать текст и сериализованные значения. Корректность проверяется во время компиляции.
fn main() { // `print!`, как `println!`, но он не добавляет новую строку в конце print!("January has "); // `{}` это заполнители для аргументов, которые будут строками println!("{} days", 31i); // `i` суффикс указывает компилятору, что этот литерал имеет тип: целое // число со знаком, смотрите следующую главу для более подробной информации // Позиционные аргументы могут быть повторно использованы по шаблону println!("{0}, this is {1}. {1}, this is {0}", "Alice", "Bob"); // Аргументы можно называть println!("{subject} {verb} {predicate}", predicate="over the lazy dog", subject="the quick brown fox", verb="jumps"); // Специальное форматирование может быть указано в заполнителе после `:`, `t` это бинарное представление println!("{} of {:t} people know binary, the other half don't", 1i, 2i); // Ошибка! Не хватает аргумента для вывода println!("My name is {0}, {1} {0}", "Bond"); // Исправьте ^ добавьте отсутствующий аргумент: "James" }
Дополнительная информация о форматировании здесь: std::fmt
3. Литералы и операторы
Целые числа 1, с плавающей точкой 1.2, символы ‘a’, строки "abc"
, логические true и блочные () типы могут быть выражены с помощью литералов.
Также целые числа можно выразить через шестнадцатеричное, восьмеричное или двоичное обозначение, используя один из префиксов: 0x, 0o или 0b.
В числовые литералы можно вставлять подчёркивания для читабельности, например, 1_000 такой же, как и 1000, а 0.000_001 такой же, как и 0.000001.
Мы должны сказать компилятору, какой из литералов мы используем. Сейчас мы будем использовать суффикс u
, указывающий, что литерал является целым числом без знака, суффикс i
чтобы указать, что это знаковое целое число. Мы рассмотрим систему типов в 5 главе, а также подробную информацию о аннотировании литералов.
Доступные операторы и их приоритет похож на C-подобных языках.
fn main() { // Целочисленное сложение println!("1 + 2 = {}", 1u + 2); // Вычитание println!("1 - 2 = {}", 1i - 2); // Попробуйте изменить `1i` на `1u` и понять, почему тип важен // Булева логика println!("true AND false is {}", true && false); println!("true OR false is {}", true || false); println!("NOT true is {}", !true); // Битовые операции println!("0011 AND 0101 is {:04t}", 0b0011u & 0b0101); println!("0011 OR 0101 is {:04t}", 0b0011u | 0b0101); println!("0011 XOR 0101 is {:04t}", 0b0011u ^ 0b0101); println!("1 << 5 is {}", 1u << 5); println!("0x80 >> 2 is 0x{:x}", 0x80u >> 2); // Используйте подчеркивания, чтобы улучшить читаемость println!("One million is written as {}", 1_000_000u); }
4. Переменные
Значения (как и литералы) могут быть связаны с переменными, используя обозначение let.
fn main() { let an_integer = 1u; let a_boolean = true; let unit = (); // скопировать значение `an_integer` в `copied_integer` let copied_integer = an_integer; println!("An integer: {}", copied_integer); println!("A boolean: {}", a_boolean); println!("Meet the unit value: {}", unit); // Компилятор предупреждает о неиспользуемых переменных; эти предупреждения можно // отключить используя подчёркивание перед именем переменной let _unused_variable = 3u; let noisy_unused_variable = 2u; // Исправьте ^ Добавьте подчёркивание }
4.1 Изменчивость
По-умолчанию переменные нельзя изменять, но это можно исправить добавив модификатор mut.
fn main() { let _immutable_variable = 1i; let mut mutable_variable = 1i; println!("Before mutation: {}", mutable_variable); // Ок mutable_variable += 1; println!("After mutation: {}", mutable_variable); // Ошибка! _immutable_variable += 1; }
Компилятор будет выводить сообщения об ошибке изменчивости.
4.2 Области и видимость
Переменные имеют локальную область, и имеет видимость в блоке (блок представляет собой набор операторов, заключённых в фигурные скобки {}). Кроме того, допускается скрытие переменной.
fn main() { // Эта переменная живет в области функции main let long_lived_variable = 1i; // Это блок, он имеет меньший объем нежели основная функция { // Эта переменная существует только в этом блоке let short_lived_variable = 2i; println!("inner short: {}", short_lived_variable); // Эта переменная не видна внешней функции let long_lived_variable = 5_f32; println!("inner long: {}", long_lived_variable); } // Конец блока // Ошибка! `short_lived_variable` не существует в этой области println!("outer short: {}", short_lived_variable); // Исправьте ^ Заккоментируйте строку println!("outer long: {}", long_lived_variable); }
4.3 «Первое объявление»
Можно сперва объявлять переменные, а инициализировать их позже. Но эта форма редко используется, так как это может привести к использованию неинициализированных переменных.
fn main() { // Объявляем переменную let a_variable; { let x = 2i; // Инициализируем переменную a_variable = x * x; } println!("a variable: {}", a_variable); let another_variable; // Ошибка! Использование неинициализированной переменной println!("another variable: {}", another_variable); // Исправьте ^ Заккоментируйте строку another_variable = 1i; println!("another variable: {}", another_variable); }
Компилятор запрещает использование неинициализированных переменных, так как это привело бы к непредсказуемым последствиям.
5. Типы
Rust обеспечивает безопасность с помощью статической проверки типов. Типы переменных могут быть аннотированы, когда объявляются. Тем не менее, в большинстве случаев, компилятор сможет определить тип переменной из контекста, это снижает время их аннотации.
fn main() { // Аннотированный тип переменной let a_float: f64 = 1.0; // Эта переменная типа `int` let mut an_integer = 5i; // Ошибка! Тип переменной нельзя изменять an_integer = true; }
Это краткое изложение примитивных типов в Rust:
- целые числа:
i8
,i16
,i32
,i64
иint
(определяет компьютер) - целые числа без знака:
u8
,u16
,u32
,u64
иuint
(определяет компьютер) - с плавающей точкой:
f32
,f64
char
значения Unicode:'a', 'α'
и'∞'
(4 байта каждый)bool
true или false- тип блока
()
5.1 Приведение типов
Rust не предоставляет неявного преобразования типов (coercion) между примитивными типами. Но, явное приведение типов (casting) может быть достигнуто с помощью ключевого слова as.
fn main() { let decimal = 65.4321_f32; // Ошибка! Нет неявного преобразования let integer: u8 = decimal; // Исправьте ^ Заккоментируйте строку // Явное преобразование let integer = decimal as u8; let character = integer as char; println!("Casting: {} -> {} -> {}", decimal, integer, character); }
5.2 Литералы
В числовых литералах тип может быть аннотирован, добавив тип в качестве суффикса, за исключением uint
, использующей суффикс u
и int
, который использует суффикс i
.
Тип литералов без суффикса будет зависеть от того, как они используются. Если никаких ограничений не существует, то компилятор выдаст сообщение об ошибке.
fn main() { // Литералы с суффиксами, их вид известен при инициализации let x = 1u8; let y = 2u; let z = 3f32; // Литералы без суффикса, их вид зависит от того, как они используются let i = 1; let f = 1.0; // `size_of_val` возвращает размер переменной в байтах println!("size of `x` in bytes: {}", std::mem::size_of_val(&x)); println!("size of `y` in bytes: {}", std::mem::size_of_val(&y)); println!("size of `z` in bytes: {}", std::mem::size_of_val(&z)); println!("size of `i` in bytes: {}", std::mem::size_of_val(&i)); println!("size of `f` in bytes: {}", std::mem::size_of_val(&f)); // Ограничения (слагаемые должны иметь тот же тип) для `i` и `f` let _constraint_i = x + i; let _constraint_f = z + f; // Заккоментируйте эти две строки }
Есть некоторые понятия, используемые в предыдущем коде, которые не были объяснены раньше, вот краткое объяснение для нетерпеливых читателей:
fun(&foo)
используется, чтобы передать аргумент в функцию по ссылке, а не по значениюfun(foo)
.std::mem::size_of_val
является функцией, но вызывается с указанием полного пути. Код можно разделить на логические единицы, называемые модулями. Здесь функцияsize_of_val
определена в модулеmem
, а модульmem
определен в крэйтеstd
.
5.3 Логический вывод
Логический вывод типов довольно умён. Тип добавляемой переменной используется как определитель типа для второй переменной. Вот продвинутый пример:
fn main() { // Использование локального вывода, компилятор знает, что `elem` имеет тип `u8` let elem = 5u8; // Создадим пустой вектор (расширяемый массив) let mut vec = Vec::new(); // В этот момент компилятор не знает точный тип `vec`, он // просто знает, что это вектор `Vec<_>` // Вставим `elem` в вектор vec.push(elem); // Ага! Теперь компилятор знает, что `vec` это вектор `u8` (`Vec<u8>`) // Попробуйте заккоментировать строку `vec.push(elem)` println!("{}", vec); }
Отсутствует необходимость в аннотации типа переменной, компилятор счастлив как и программист!
5.4 Псевдонимы (алиасы)
Оператор type
может быть использован, чтобы задать новое имя существующему типу. Тип должен быть в стиле CamelCase, либо компилятор выдаст предупреждение. Исключением из этого правила являются примитивные типы: uint
, f32
и другие.
// `NanoSecond` это новое имя для `u64` type NanoSecond = u64; type Inch = u64; // Используйте этот атрибут, чтобы не выводить предупреждение #[allow(non_camel_case_types)] type uint64_t = u64; // Попробуйте удалить атрибут fn main() { // `NanoSecond` = `Inch` = `uint64_t` = `u64` let nanoseconds: NanoSecond = 5 as uint64_t; let inches: Inch = 2 as uint64_t; // Обратите внимание, что псевдонимы новых типов не предоставляют // дополнительную безопасность, из-за того, что они не нового типа println!("{} nanoseconds + {} inches = {} unit?", nanoseconds, inches, nanoseconds + inches); }
Основное применение псевдонимов это снижение количества кода, например, тип IoResult<T>
является псевдонимом типа Result<T, IoError>
.
Заключение
Присоединяйтесь к google-группе: Rust по-русски для получения дополнительной информации по этому языку.
Все замечания, ошибки или неточности отправляйте мне в почту.
ссылка на оригинал статьи http://habrahabr.ru/post/232829/
Добавить комментарий