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

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

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

Класс языка:

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

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

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

Появился в:

2010[1]

Автор:

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

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

.rs

Выпуск:

1.6[2] (21 января 2016 года)

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

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

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

rustc

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

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

Повлиял на:

Swift

Лицензия:

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

Сайт:

rust-lang.org

ОС:

GNU/Linux, OS X, Windows, FreeBSD, iOS и Android

Rust — мультипарадигмальный компилируемый язык программирования общего назначения, разрабатываемый 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 объявила о присоединении Samsung к проекту Servo — браузерному движку нового поколения, при активном участии которой код движка Servo был портирован на 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]

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

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

Язык Rust имеет встроенные простые типы:

Целочисленные типы
i8, u8, i16, u16, i32, u32, i64, u64, а также isize и usize, имеющие размер указателя на данной платформе,
Булев
bool,
Символ
char, представляющий символ Unicode,
Числа с плавающей точкой
f32, f64,
Массив,
Срез массива (slice)
Ссылка на непрерывную часть массива, фактически содержит ссылку на первый элемент среза и на длину среза,
Строки
Тип String, владеющий содержимым, и строковый срез str, гарантированно содержащие валидную UTF-8 строку,
Кортеж
Подобно структуре, содержит произвольное количество разнотипных полей, но поля безымянны, обращение к полям возможно по индексу. Кортежи - безымянные типы: кортежи с одинаковым количеством и типами полей являются совместимыми по типу. С помощью ключевого слова type можно задать псевдоним, который, однако, не задает нового типа,
Функция
Объекты-функции имеют тип, определяемый их сигнатурой, т. е. параметрами и возвращаемым значением.

Возможности объявления пользовательских типов:

Структура
Структуры в языке Rust практически не отличаются от структурных типов других языков. Структура объявляет именованные поля данных (методы могут быть объявлены отдельно с помощью ключевого слова impl). Согласно теории типов являются типом-произведением,
Перечисление (enum)
Аналогично объединению (union) C и C++, поскольку разделяют одну и ту же память для разных типов данных, но запоминают сохраненный тип (tagged union) и не позволяют обращаться к этим данным как к другому типу. По классификации теории типов являются типом-суммой (tagged union). Обычное перечисление является вырожденным случаем типа-суммы,
Кортеж-структура
гибридный тип, обладающий свойствами кортежа и структуры: именованный тип с безымянными полями.

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

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

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

Move-семантика
По умолчанию объект «переносится» (move) новому владельцу при присваивании. При этом происходит простое побайтовое копирование (в случае оптимизации - не происходит и этого), старая связка при этом становится недействительной.
Заимствования (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;    // неизменяемая ссылка на изменяемое связывание

*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; комментарии также пишутся в С-формате; имена модулей разделяются двумя символами двоеточия (::).

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

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

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

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

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

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

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

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

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

Hello, world!:

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

Поиск факториала, в рекурсивном и итеративном стилях:

// Ветви условия в данной функции демонстрируют необязательный вариант
// неявного возврата значений, что может быть удобно при использовании 
// "функционального" стиля. В отличие от C++ и других родственных языков, 
// в Rust if - это выражение, возвращающее значение, а не просто оператор. 
fn fac_recur(n: i32) -> i32 {
    if n <= 1 { 1 }
    else { n * fac_recur(n-1) }
}

fn fac_iter(n: i32) -> i32 {

    // Переменные объявляются с ключевым словом "let", по умолчанию
    // не изменяемые. Если нужна их изменяемость, добавляется ключевое
    // слово 'mut'.
    let mut i = 1;
    let mut result = 1;
    while i <= n {
        result *= i;
        i += 1;
    }
    return result;    // Пример явно возвращаемого значения.
}

fn fac_lambda(n: i32) -> i32 {

    // Итераторы включают множество методов трансформации.
    // "|accum, x|" - это определение анонимной функции.
    // Оптимизации компиляции преобразуют эту функцию 
    // в нечто, похожее на итеративный вариант.
    (1..n+1).fold(1, |accum, x| accum * x)
}

fn main() {
    println!("Recursive result: {}", fac_recur(10));
    println!("Iterative result: {}", fac_iter(10));
    println!("Lambda result: {}", fac_lambda(10));
}

Демонстрация возможностей параллельных вычислений в Rust:

use std::thread;

// Функция создаёт 10 "задач", которые выполняются параллельно.
// Чтобы убедиться в параллельности, запустите программу несколько раз,
// и обратите внимание на непостоянный порядок вывода результатов.
fn main() {

    // Эта строка неизменяемая, поэтому может безопасно использоваться
    // из нескольких задач.
    let greeting = "Привет";
    let mut threads = Vec::new();

    // Цикл for работает с любым типом, реализующим
    // интерфейс (trait) Iterator.
    for num in 0..10 {
        threads.push(thread::spawn(move || {

            // println! - это статический макрос, выполняющий форматирование
            // строки и ее вывод. Макросы структурные (как в Scheme), а не
            // текстовые (как в Си).
            println!("{} из потока номер {}", greeting, num);

        }));
    }
    for thread in threads {
        let res = thread.join();
        if let Err(_) = res {
            println!("Произошла ошибка!");
        }
    }
}

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

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

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

  1. https://mail.mozilla.org/pipermail/rust-dev/2010-July/000001.html
  2. Rust Releases.
  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. The Rust Reference Manual (англ.). Официальный сайт Rust (2014). Проверено 19 сентября 2014.

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

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