Rust (язык программирования)

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск
Rust programming language black logo.svg
Семантика:

мультипарадигмальный: параллельное, функциональное, объектно-ориентированное, императивное, структурное программирование

Класс языка:

язык программирования и свободное программное обеспечение

Тип исполнения:

компилируемый

Появился в:

июль 2010[1]

Автор:

Грэйдон Хор, разработчики проекта Rust

Расширение файлов:

.rs, .rlib

Выпуск:

1.10[2] (7 июля 2016 года)

Система типов:

статическая, строгая, с выводом типов, опционально динамическая

Основные реализации:

rustc

Испытал влияние:

Alef, C++, Camlp4, Common Lisp, Erlang, Haskell, Hermes, Limbo, Napier, Napier88, Newsqueak, NIL, Sather, OCaml, Standard ML, Cyclone, Scheme, Swift, C#, Ruby[3]

Повлиял на:

Swift, Crystal

Лицензия:

Apache License 2.0[4] и Лицензия MIT[4]

Сайт:

rust-lang.org

ОС:

GNU/Linux[d], macOS, Windows, FreeBSD, iOS и Android

Rust (англ. rustржавчина, произносится [rʌst]?раст) — мультипарадигмальный компилируемый язык программирования общего назначения, спонсируемый Mozilla Research[5], поддерживающий функциональное программирование, модель акторов, процедурное программирование, объектно-ориентированное программирование.

Rust — это системный язык программирования, внимание которого сосредоточено на трёх задачах: безопасность, скорость и параллелизм. Он сопоставим по скорости и возможностям с C++, однако, даёт большую безопасность при работе с памятью, что обеспечивается механизмами ограничения. Rust также направлен на достижение «абстракции с нулевой стоимостью»[6].

После нескольких лет активной разработки первая стабильная версия (1.0) вышла 15 мая 2015 года, после чего новые версии выходят раз в 6 недель[7]. Программы, написанные на ранних стабильных версиях языка будут собираться и на более новых стабильных версиях.

История[править | править вики-текст]

  • Работа над языком была начата Грэйдоном Хором в 2006 году, в 2009[8] к разработке подключилась Mozilla, и в 2010 году язык был официально представлен на Mozilla Summit 2010[9]. Также в 2010 году разработка языка была переведена с использования компилятора, написанного на OCaml, на компилятор, написанный на Rust, с использованием LLVM в качестве back-end[10]. В следующем году он успешно скомпилировал сам себя[11].
  • Январь 2012 г.: выпуск альфа-версии (0.1) компилятора Rust[12].
  • Апрель 2013 г.: выпуск версии 0.6. Одновременно с этим, Mozilla объявила о присоединении к проекту Servo — браузерному движку нового поколения — компании Samsung, при активном участии которой код движка был портирован на ARM архитектуру[13].
  • Январь 2015 г.: выпуск версии Rust 1.0 Alpha, было стабилизировано ядро языка и большая часть стандартных библиотек; переработана и улучшена система макросов; после долгих обсуждений переименованы типы integer: int и uint стали isize и usize; убрана недоделанная система «зелёных» потоков[14].
  • Апрель 2015 г.: выпуск версии Rust 1.0 Beta, язык стабилизирован, до финального релиза возможны небольшие правки в API[15].
  • Май 2015 г.: выпуск версии Rust 1.0. Программные интерфейсы и возможности языка подверглись значительной ревизии, после которой по умолчанию оставлены только полностью готовые к применению возможности, реализация которых не будет изменяться в дальнейшем. Все остальные функции переведены в разряд экспериментальных и вынесены из поставки по умолчанию.[16]
  • В 2015 Rust признан третьим самым любимым языком по версии Stack Overflow,[17] и в 2016 он переместился на первое место.[18]

Обзор[19][править | править вики-текст]

Система типов[править | править вики-текст]

Особенности[править | править вики-текст]

  • Статическая типизация.
  • Определение типов по предположению — в объявлениях локальных переменных указание типа не является обязательным.
  • Rust позволяет объявлять типы, которые не занимают места. Примеры: struct Foo;, (), [u8; 0][20].
  • Rust также позволяет объявлять типы, экземпляр которых нельзя создать. Примеры: enum Void {}.

Основные типы[править | править вики-текст]

В Rust все типы можно разделить на две основные группы:

  • Простые типы (типы постоянной длины, встроенные в сам язык): Числовой, Булев, Символ, Массив, Срез, Строковый срез, Кортеж, Ссылка, Указатель на функцию[21][22]. Из простых типов можно выделить «Машинные» типы (реализованные непосредственно в современных процессорах): Числовой, Булев, Символ.
  • Типы стандартной библиотеки std (переменной длины): Вектор, Строка, Хеш-таблица.
Числовой
  • Целое (integer). i8, u8, i16, u16, i32, u32, i64, u64, а также isize и usize, имеющие размер указателя на данной платформе. Примеры значений: -5i8, 0x400u16, 0o100i16, 20_922_789_888u64, b'*' (u8 char), 42 (по предположению), 0xfff_fc00usize,
Булев (bool)
true, false,
Символ (char)
Тип, представляющий символ Unicode, (внутреннее представление данных как u32). Примеры значений: '*', '\n', '\x7f', '\u{CA0}',
Указатель на функцию (function pointer)
Объекты-функции имеют тип, определяемый их сигнатурой, т. е. параметрами и возвращаемым значением. Пример: let f: fn(i32) -> i32 = plus_one;
Ссылка
  • Ссылка (разделяемое заимствование - shared borrow) &T (разделяемая, не изменяемая, не владеющая ресурсом), вместо того, чтобы забирать владение ресурсом, она его заимствует. Имена, которые заимствуют что-то, не освобождают ресурс, когда они выходят из области видимости. Кроме того, имена-владельцы переходят в заимствованное состояние[23].
  • Ссылка изменяемая (изменяемое заимствование - mutable borrow) &mut T (не владеющая ресурсом). Позволяет изменять ресурс, который заимствуется.
Коллекция
  • Массив (array) [T; N] — последовательность элементов одного и того же типа, имеющая фиксированный размер. Примеры: [1, 2, 3], [true, 10000].
  • Вектор (vec, vector) Vec<T>  — это динамический или, по-другому, «растущий» массив, реализованный в виде стандартного библиотечного типа Vec<T>. Примеры: vec![0; 10];, Vec::with_capacity(10)
  • Срез (slice, view) &[T]  — это ссылка (или «проекция») на другую структуру данных. Они полезны, когда нужно обеспечить безопасный, эффективный доступ к части массива без копирования. Пример: &a[1..4],
  • Кортеж (tuple) (T1, T2, T3, ...). Подобно структуре, содержит произвольное количество разнотипных полей, но поля безымянны, обращение к полям возможно по индексу (t.0, t.1). Кортежи — безымянные типы: кортежи с одинаковым количеством и типами полей являются совместимыми по типу. С помощью ключевого слова type можно задать псевдоним, который, однако, не задает нового типа,
  • Кортеж нулевой длины ((); пустой кортеж) часто называют «единичным значением». Соответственно, тип такого значения — «единичный тип». В Rust нет специального типа, соответствующего «NULL», и вместо этого используется пустой кортеж. Если функция не возвращает значение, то считается, что она возвращает ().
  • Хеш-таблица (HashMap) HashMap<T1, T2> - это структура данных, реализующая интерфейс ассоциативного массива, а именно, она позволяет хранить пары (ключ, значение) и выполнять три операции: операцию добавления новой пары, операцию поиска и операцию удаления пары по ключу. Хеш-таблицы в Rust похожи на Векторы, и хранят свои значения не по индексу, а по ключу. Пример: HashMap::new();
Строка
  • Строка (String) (внутреннее представление данных как Vec<u8>) — тип, владеющий содержимым. String представляет собой строку, размещенную в куче. Эта строка расширяема, и она гарантированно является последовательностью UTF-8. String обычно создаётся путем преобразования из строкового среза с использованием метода to_string. Примеры: "строковый срез".to_string(), String::new().
  • «Строковый срез» (string slice, string literal) &str, &'static str, &[u8]. Частный вид среза. Строковые срезы имеют фиксированный размер и не могут быть изменены. Они представляют собой ссылку на последовательность байт UTF-8. Пример: "строковый срез". &'static str - строка, введённая символами в коде самой программы, - тот же строковый срез, только статически размещённый (сохраняемый в скомпилированной программе).

Возможности объявления пользовательских (составных) типов[править | править вики-текст]

Структура (struct)
  • Структура Си-подобная. Пример: struct Color {red: u8, green: u8, blue: u8}
  • Структура-кортеж. Пример: struct Color (u8, u8, u8);
  • Структура - «единичное» значение. Пример: struct Electron;
Перечисление (enum)
Каждый вариант в перечислении в Rust может быть также связан с другими данными. Синтаксис для объявления вариантов схож с синтаксисом для объявления структур: могут быть варианты без данных, варианты с именованными данными и варианты с безымянными данными:
  • Вариант с «единичным» значением: enum Day {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}
  • Вариант с конкретным значением: enum Foo {Bar = 123,}.
  • Вариант - структура (именованные данные): enum Message {Quit, Move { x: i32, y: i32 }}.
  • Вариант - кортежная структура (безымянные данные): enum Message {Quit, Size(i32)}.
Константа
  • const - Постоянные. Живут в течение всего времени работы программы. А именно, у них вообще нет определённого адреса в памяти. Это потому, что они встраиваются (inline) в каждое место, где есть их использование,
  • static - значение с возможностью изменения, имеющее время жизни 'static. Похожи на постоянные, но статические значения не встраиваются в место их использования. Это значит, что каждое значение существует в единственном экземпляре, и у него есть определённый адрес.

Управление памятью[править | править вики-текст]

В Rust реализована «умная» модель памяти, поддерживающая эффективные структуры данных и безопасные шаблоны параллельного выполнения, а также полностью запрещающая некорректный доступ к памяти, который обычно является источником критических ошибок сегментации в других языках программирования. Отсутствие null-указателей, контроль за использованием неинициализированных и деинициализированных переменных; невозможность совместного использования разделяемых состояний несколькими задачами; статический анализ времени жизни указателей; проверка на выход за пределы массива (автоматически и всегда).

Модель памяти Rust можно охарактеризовать следующими терминами.

Move-семантика
По умолчанию объект «переносится» (move) новому владельцу при присваивании. При этом происходит простое побайтовое копирование (в случае оптимизации - не происходит и этого), старая связка при этом становится недействительной.
                                                                                       
let a = "объект с данными в куче".to_string();
// объект передан переменной b
// переменная a становится неинициализированной                                                                   
let b = a;
// ошибка!                                                                                        
let c = a;
// данные объекта на стеке                                                                                       
let a = 55
// копия объекта передана переменной b                                                                   
let b = a;
// c = 55                                                                                        
let c = a;
Заимствования (borrow) с возможностью изменения заимствованного объекта (&mut) и без таковой (&)
Лексически и семантически очень схожи со ссылками, но имеют специфику: заимствование объекта сходно семантике «Либо много читателей, либо один писатель» — объект можно передать в заимствование либо однократно с возможностью изменения объекта, либо многократно без таковой; заимствования можно перезаимствовать другому заемщику. В отличие от настоящей семантики «Либо много читателей, либо один писатель», применяется не в контексте синхронизации потоков, а универсально. Контроль корректности заимствований происходит во время компиляции и не порождает дополнительного исполнимого кода (принцип zero-cost abstractions). Компилятором контролируется также соотношение времен жизни заимствований и самого объекта — заимствования не могут жить (выходить за пределы области видимости) заимствованного объекта. Заимствования работают с любыми данными независимо от их размещения (стек, локальная или обменная куча, другие специальные расположения). Следует различать независимые понятия — изменяемость собственно заимствования (let mut b = &c) и изменяемость заимствованного объекта (let b = &mut c).
Упаковка (Box)
Простой умный указатель, владеющий объектом в куче. Уничтожает объект и освобождает память при выходе из области видимости.
Ячейка (Cell, RefCell)
Реализует изменяемость содержимого при неизменяемости самой ячейки.
Указатели со счётчиком ссылок (Rc<T>) и с атомарным счётчиком ссылок (Arc<T>)
Умные указатели с подсчетом ссылок, уничтожающие объект и освобождающие память при обнулении счетчика. Arc реализует потокобезопасность для счетчика ссылок (но не для самого объекта). Rc и Arc контролируют неизменяемый объект, поэтому типичное их использование выглядит как Rc<Cell<T>> в однопоточной программе и Arc<Mutex<T>> в многопоточной.
Сырые указатели неизменяемые (*const T) и изменяемые (*mut T)
Указатели без гарантии безопасности. Настоятельно не рекомендуется их использовать.

Связывания неизменяемы по умолчанию, а чтобы объявить переменную изменяемой, необходимо ключевое слово mut.

Примеры:

let x = 80;    // связывание владельца x со значением 80
let mut y = 50;    // изменяемое связывание
let z = &x;    // неизменяемая ссылка на неизменяемое связывание
let w = &mut y;    // неизменяемая ссылка на изменяемое связывание
let r = &mut y;    // error: нельзя создавать вторую ссылку на изменяемое связывание

*w = 90    // y = 90
*z = 30    // error: попытка изменения через ссылку на неизменяемое связывание

let n = Box::new(42);    // упаковка
let m = Rc::new(55);    // счётчик ссылок
let data = Arc::new("test_string")    // атомарный счётчик

Синтаксис[править | править вики-текст]

Синтаксис Rust похож на Си и C++ с блоками кода, разделёнными фигурными скобками, и такими управляющими ключевыми словами, как if, else, while, и for; комментарии также пишутся в С-формате; имена модулей разделяются двумя символами двоеточия (::).

Набор операторов в Rust: арифметические (* — умножение, / — деление, % — взятие остатка от деления, + — сложение, - — вычитание и унарный префиксный оператор - для смены знака числа), битовые (>>, <<, &, | и ^), операторы сравнения (==, !=, <, >, <=, >=), логические (and или && и or или ||). Для приведения типов в Rust используется бинарный оператор as (неявное приведение типов полностью отсутствует).

Rust поддерживает макроопределения — средства подстановки с использованием регулярных выражений, выполняющиеся во время этапа подготовки к компиляции, более развитые и безопасные, чем в Си. Макроопределения (макрокоманды) — это определяемые пользователем простые расширения синтаксиса, выполняемые с помощью команды macro_rules! Макрокоманды определяются в том же стиле, что и конструкция сравнения с образцом. Признак макроса — восклицательный знак в конце имени.

Связывание имён[править | править вики-текст]

Ключевое слово let определяет связывание (локальную переменную).

let x: i32 = 5;

Данная запись обозначает: «x — это связывание типа int со значением пять».

Шаблоны сопоставления (match)[править | править вики-текст]

В языке Rust конструкция match представляет собой обобщённую и усовершенствованную версию конструкции switch языка C. Более того, match является самым мощным, универсальным и можно даже сказать ключевым элементом управления не только потоком выполнения, но и структурами данных в языке.

match x {
    1 | 2 => println!("один или два"),
    3 => println!("три"),
    _ => println!("что угодно"),
}

Деструктуризация. При работе с составным типом данных, вроде struct, можно разобрать его на части («деструктурировать») внутри шаблона:

match origin {
    Point { x, y } => println!("({},{})", x, y),
}

Unsafe[править | править вики-текст]

В блоках и функциях, помеченных unsafe, компилятор разрешает делать лишь три дополнительные вещи[24]:

  • обновлять изменяемые статические (static mut) переменные;
  • разыменовывать сырые указатели;
  • вызывать небезопасные (unsafe) функции.

Объектная система[править | править вики-текст]

В Rust объектная система основана на типажах (traits) и структурах данных. Типажи определяют типы, а также соответствующие методы и реализации. Типаж может содержать реализации методов, принимаемые по умолчанию. Реализация типажей для данной структуры, а также реализация собственных методов структуры обозначается ключевым словом impl. Язык содержит несколько десятков предопределенных типажей, большая часть которых используется для перегрузки операторов, а некоторые имеют специальное значение.

Rust поддерживает обобщённые типы. Помимо функций, обобщёнными в Rust могут быть комплексные типы данных, структуры и перечисления. Компилятор Rust компилирует обобщённые функции весьма эффективно, применяя к ним мономорфизацию (генерация отдельной копии каждой обобщённой функции непосредственно в каждой точке её вызова). Таким образом, копия может быть адаптирована под конкретные типы аргументов, а следовательно, и оптимизирована для этих типов. В этом отношении обобщённые функции Rust сравнимы по производительности с шаблонами (templates) языка C++.

Параллельные вычисления[править | править вики-текст]

В более ранних версиях языка поддерживались легковесные потоки, но потом от них отказались в пользу нативных потоков операционной системы. При этом рекомендуемым методом обмена данными между потоками является отправка сообщений, а не использование общей памяти. Для достижения высокой производительности возможно отправлять данные не через копирование, а используя собственные указатели (Box<T>). Они гарантируют только одного владельца.

Примеры программ[править | править вики-текст]

В статье «Примеры программ на языке программирования Rust» собраны примеры небольших программ, демонстрирующих некоторые возможности языка Rust и его стандартной библиотеки.

Hello, world!:

fn main() {
    println!("Hello, world!");
}

Сравнение с другими языками[править | править вики-текст]

  • Принципы работы с памятью Rust ощутимо отличаются как от языков с полным доступом к памяти, так и от языков с полным контролем за памятью со стороны сборщика мусора. Модель памяти Rust построена таким образом, что, с одной стороны, предоставляет разработчику возможность контролировать, где размещать данные, вводя разделение по типам указателей и обеспечивая контроль за их использованием на этапе компиляции. C другой стороны, механизм подсчёта ссылок Rust старается выдавать ошибки компиляции в тех случаях, в которых использование прочих языков приводит к ошибкам времени выполнения или аварийному завершению программ.
  • Rust позволяет объявлять функции и блоки кода как «небезопасные» (unsafe). В области такого небезопасного кода не применяются некоторые ограничения, таким образом можно выполнять операций на более низком уровне; но, разработчик должен на 100% понимать что он делает.

Применение[править | править вики-текст]

Среди компаний, активно использующих Rust в продакшене, можно отметить Mozilla и DropBox.[25]

Примечания[править | править вики-текст]

  1. https://mail.mozilla.org/pipermail/rust-dev/2010-July/000001.html
  2. Announcing Rust 1.10.
  3. The Rust Reference Manual#Influences (англ.). Официальный сайт Rust (2014). Проверено 22 сентября 2014.
  4. 1 2 https://github.com/rust-lang/rust/blob/master/COPYRIGHT
  5. The Rust Language (англ.). Lambda the Ultimate (8 July 2010). Проверено 30 октября 2010. Архивировано из первоисточника 23 ноября 2012.
  6. Ivo Balbaert. Rust Essentials. — Packt Publishing, May 2015. — ISBN 978-1-78528-576-9.
  7. The Rust Core Team. Announcing Rust 1.0 (англ.). The Rust Programming Language Blog (15 May 2015). Проверено 18 августа 2015.
  8. Project FAQ (англ.). Официальный сайт Rust (2014). Проверено 17 апреля 2012.
  9. Brendan Eich. Future Tense (англ.) (29 April 2011). — «At Mozilla Summit 2010, we launched Rust, a new programming language motivated by safety and concurrency for parallel hardware, the “manycore” future which is upon us.»  Проверено 17 апреля 2012. Архивировано из первоисточника 18 сентября 2012.
  10. Graydon Hoare. Rust Progress (англ.) (2 October 2010). Проверено 17 апреля 2012. Архивировано из первоисточника 18 сентября 2012.
  11. Graydon Hoare. [rust-dev] stage1/rustc builds (англ.) (20 April 2011). — «After that last change fixing the logging scope context bug, looks like stage1/rustc builds. Just shy of midnight :)»  Проверено 17 апреля 2012.
  12. Brian Anderson. The Rust compiler 0.1 is unleashed (англ.). Списки рассылки Mozilla (20 January 2012). Проверено 22 сентября 2014.
  13. Brendan Eich. Mozilla and Samsung Collaborate on Next Generation Web Browser Engine (англ.). Официальный блог Mozilla (3 April 2013). Проверено 22 сентября 2014.
  14. Announcing Rust 1.0 Alpha.
  15. Announcing Rust 1.0 Beta.
  16. Announcing Rust 1.0.
  17. Stack Overflow Developer Survey 2015
  18. Stack Overflow Developer Survey 2016 Results
  19. The Rust Reference Manual (англ.). Официальный сайт Rust (2014). Проверено 19 сентября 2014.
  20. Exotically Sized Types
  21. std - Rust
  22. The Rust Reference
  23. The Rust Reference
  24. Unsafe
  25. https://www.rust-lang.org/en-US/friends.html

Литература[править | править вики-текст]

Ссылки[править | править вики-текст]