Рубріки: Теория

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

Андрей Галадей

На сегодняшний день существует уйма языков программирования. Одни из них старые и заслуженные, такие как C/C++, другие — просто модные. Но есть и такие, которые, помимо моды, новизны и популярности, еще и весьма перспективные. И сегодня мы поговорим об одном таком языке. Это язык Rust (неофициально именуемый «Ржавым»), который по состоянию на 2021 год был самым высокооплачиваемым языком в мире.

Итак, сегодня мы подробно поговорим о том, какие особенности имеет этот язык, где и в каких задачах применяется, какими преимуществами и недостатками обладает. Поехали!

Содержание:
1. Что такое Rust?
2. Особенности языка Rust
3. История языка программирования Rust
4. Преимущества и недостатки языка Rust
5. Основы синтаксиса Rust
5.1. Первая программа
5.2. Переменные в Rust
5.3. Типы данных
5.4. Функции
5.5. Комментарии
5.6. Условные инструкции
5.7. Владение
5.8. Структуры
6. Сферы применения
Итоги

1. Что такое Rust?

Язык программирования Rust — это универсальный ЯП, который разрабатывают в компании Mozilla. Он основан на трех «китах» — скорости, безопасности и эргономике. Этот язык весьма молод — он релизнулся в 2015 году (первая версия вышла 15 мая), но при этом активно развивается. Авторы позиционируют его в качестве одного из вероятных наследников C/C++. И тому есть немало причин.

Несмотря на новизну языка, каждые 6 недель выходит новый релиз, добавляющий новые возможности. Разработка идет очень активно, права на язык принадлежат Rust Foundation, который учредили компании AWS, Huawei, Google, Microsoft и Mozilla. Отметим, что в период с 2016 по 2020 год Rust занимал первое место в списке любимых языков по итогам ежегодного опроса разработчиков Stack Overflow Developer Survey.

2. Особенности языка Rust

Язык программирования Rust изначально разрабатывался как быстрый и лаконичный. Потому первой его особенностью является синтаксис, который похож на синтаксис C. Программы на этом языке также выполняются быстро.

Еще одна особенность — это высокий уровень защищенности данных в памяти. Язык самостоятельно управляет размещением данных в памяти, используя указатели. Это позволяет избежать многих ошибок, связанных с переполнением стека. Для этого задействована модель управления памятью на основе безопасных шаблонов параллельного выполнения.

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

В языке также нет garbage collection или сборщика мусора, который присутствует во многих других языках. Эта технология автоматически очищает память от ненужных уже объектов. Но в Rust, как сказано выше, используется система указателей, так что сборка мусора попросту не нужна.

ЯП Rust используется не только для прикладного, но и для системного программирования. Подробнее о том, где и как он применятся, мы поговорим дальше, а пока отметим, что на этом языке можно писать даже ядро операционной системы. На данный момент есть проект перевода ядра Linux на Rust. Помимо этого, в 2019 году Microsoft начала разработку своего языка на базе Rust, на который потенциально могут перевести и Windows.

3. История языка программирования Rust

Сам язык начал разрабатываться с 2006 года сотрудником Mozilla Грэйдоном Хором, проектом он занимался в свободное время. Три года спустя проект получил финансирование от компании, а в следующем, 2010 году, его представили на Mozilla Summit 2010. Первая релизная версия появилась в 2015 году.

Название автор взял от грибов семейства «ржавчинные». По словам Хора, их особенность в том, что это «распределенные организмы», которые лишены «единой точки отказа» и могут выживать в самых разных условиях. Эти грибы также умеют быстро расти и распространяться.

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

evrone.ru

После того, как Хор показал свои наработки в компании, там заинтересовались проектом, поскольку хотели перейти от С++ к другим языкам и упростить технологии. После этого начались попытки использования языка в готовых решениях. К примеру, на Rust написали браузерный движок Servo, который создавали вместе специалисты Mozilla и Samsung.

В настоящее время язык используется для написания ряда проектов.

4. Преимущества и недостатки языка Rust

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

Преимущества языка выглядят так:

  • Наличие единого компилятора, который включает в себя встроенный менеджер пакетов и их сборщик, систему генерации тестов и документации. Все это есть изначально в базовой поставке.
  • Изначально высокий уровень безопасности при работе с памятью. Система не допускает ошибок сегментации данных при работе с памятью, что автоматически повышает защищенность приложений.
  • В языке можно применять абстракции, которые упрощают ручное управление памятью.
  • При компиляции программы многие ошибки получают варианты исправления, а ошибки в шаблонах понятны и логичны. То есть, здесь нет такого, как в других языках, где надо перелопачивать код в поисках проблемы.
  • Язык хорошо совместим с Mac и Unix-подобными системами, что обеспечивает его кроссплатформенность.
  • Указатели в памяти применяются только в небезопасном коде. Если код безопасен, то в нем используются только ссылки на однозначно существующие объекты в памяти.
  • Нет сборщика мусора, который отбирает на себя часть ресурсов.
  • Программы выполняются очень быстро.

Недостатки языка тоже есть. Вот они:

  • В Rust не используются классы и системы наследования, что усложняет написание объектно-ориентированного кода.
  • Компилятор иногда весьма параноидально отслеживает обращения к памяти, что может вызывать неудобства при работе.
  • Высокий порог вхождения для разработчиков. Язык Rust создавался как замена С/С++. Потому его может быть сложно изучить новичкам в программировании, ведь язык сочетает возможности низкоуровневых и высокоуровневых языков. Однако такая сложность — обратная сторона безопасности.
  • Многие разработчики старой школы считают этот новый язык этаким «модным выскочкой» на фоне классических системных языков, что затрудняет его внедрение. Однако это уже вопрос вкуса и привычки, а не объективный недостаток.
  • Из-за активного развития многие моменты меняются от релиза к релизу, так что надо отслеживать все это в документации.
  • Некоторая избыточность кода и необходимость прописывать параметры компиляции.

Таким образом, главными плюсами Rust можно счесть скорость и безопасность, а минусы вытекают из них — жесткие требования к структуре кода, необычные решения и высокий порог вхождения.

5. Основы синтаксиса Rust

Здесь мы разберем все основные примеры синтаксических конструкций языка. И начнем с традиционного «Hello World!» на Rust.

5.1. Первая программа

Первая реальная программа на этом языке выглядит так:

fn main() {
    println!("Hello World!");
}

Разберем все части этого кода.

fn — так обозначается функция (она же function). В Rust это базовая конструкция, которая предназначена для выполнения задач. Мы объявили функцию и затем назвали ее — main (). В скобках мы указываем параметры функции, если таковые есть. Они могут быть, а могут и отсутствовать.

В фигурных скобках или { } мы указываем тело программы, то есть прописываем, что именно делает функция main (). В данном случае она выводит данные на экран с помощью макроса println!. Этот макрос похож на функцию, он добавляет новую строку и выводит данные, которые мы задали в скобках — ("Hello, world!"). Отметим, что макрос отличается от функции именно восклицательным знаком.

Точка с запятой — это конец инструкции. В данном случае это конец макроса.

5.2. Переменные в Rust

Переменные в Rust, как и в других языках, используются для хранения данных. Формат объявлений переменных таков:

let [variable_name] = [value];

Имя переменной должно быть информативным, т. е. описывать, чем является ее значение. Например:

let my_name = "Ryan";

Здесь создана переменная my_name со значением Ryan.

Рекомендуется давать переменным названия, начинающиеся со строчной буквы, а новое слово начинать с заглавной. Это не обязательно, но считается хорошим тоном. При этом в Rust переменные не меняются по умолчанию. То есть, их значение нельзя изменить после того, как они заданы.

Например, вот этот код выдаст ошибку во время компиляции:

fn main() 
{    
let x = 5;    
println!("The value of x is: {}", x);
x = 6;    
println!("The value of x is: {}", x);}

То есть, мы сначала задали значение x равное 5, а затем попытались изменить его на 6. Это неправильно.

На первый взгляд такое свойство языка Rust кажется неудобным, но оно помогает устранить баг ошибочного присваивания переменной неверного значения. Ведь такие ошибки сложно отслеживать. А безопасность, как сказано выше, это одно из основных свойств Rust.

Если же надо создать изменяемую переменную, то это делается так:

let mut x = 5;

Изменяемые переменные чаще всего используются как переменные-итераторы или как переменные в структурах цикла while. Здесь используется команда mut, чтобы четко определить, что переменная меняется.

5.3. Типы данных

В Rust можно четко указывать тип данных для переменной. Для этого используется &[type]. Если нам надо указать тип для my_name, это делается так:

let my_name = "Ryan"; // с явно определенным типом
let my_name: &str = "Ryan"; // с явно определенным типом

То есть, мы четко определили, что это будет строка, а не число.

К примеру, есть переменная answer, которая записывает ответ пользователя в форму.

let answer = "true";

Rust неявно определяет строковый тип для этой переменной, так как она приводится в кавычках.

Но при этом название переменной может указывать на булевый тип с вариантами ответа true и false. Чтобы избежать непонимания и ошибок, лучше объявить переменную явно:

let answer: bool = true;

Основные типы данных на Rust:

  • Integer: целочисленный (целые числа).
  • Float: числа с плавающей запятой (с десятичными знаками).
  • Boolean: логический (булев) с выбором между двумя вариантами (true или false).
  • String: строковый (набор символов, заключенных в кавычки).
  • Char: скалярное значение Юникод, представляющее конкретный символ.
  • Never: тип без значения (обозначается как !).

То есть, лучше использовать явные обозначения, что укладывается в парадигму безопасного языка.

5.4. Функции

Мы уже рассмотрели базовую функцию main(), но есть и другие. Функции часто представляют собой одну повторяющуюся задачу, которую можно вызывать в разных частях программы. К примеру, это addUser (добавление пользователя) или changeUsername (изменение имени пользователя).

При этом такие функции должны иметь уникальное имя и возвращать результат, а также иметь параметры. Формат выглядит так:

fn [functionName]([parameterIdentifier]: [parameterType]) 
{    
[functionBody]
}

Fn — это объявление функции.

[functionName] — имя или идентификатор, по которому еще можно будет вызвать.

() — те самые параметры, которые приводятся в скобках. Если их нет, оставляем скобки пустыми.

[parameterIdentifier] — имя передаваемого значения, которое выступает в роли переменной. Его можно вызвать в любом месте в пределах функции.

[parameterType] — тип параметра, который надо указать явно.

{} — фигурные скобки указывают на начало и конец блока кода. Этот код выполняется при каждом вызове идентификатора функции.

[functionBody] — здесь должен быть только код, связанный с выполнением задачи функции.

Таким образом, наш «Hello, world!» будет выглядеть так:

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

Как видите, все просто.

5.5. Комментарии

Комментарии в коде позволяют понять, что делает тот или иной участок кода. Компилятор не обрабатывает их, так что они нужны только для удобства программиста. Однако их желательно писать, особенно в крупных проектах, где разные люди работают над разными частями кода.

В Rust есть два способа писать комментарии. Первый — использовать двойной слэш (это по-русски называется двойной косой чертой) //. В этом случае все до конца строки игнорируется компилятором. Например:

fn main() {    // Эта строка полностью игнорируется    
println!("Hello, world!"); // А эта напечатала сообщение  
// Все готово, программа завершена
}

Второй способ — начинать комментарий косой чертой со звездочкой /* и завершать его звездочкой с косой чертой */. Такой подход позволяет размещать комментарий в строках кода и использовать несколько строк.

fn main(/* я могу это сделать! */) 
{    
/* первый комментарий  */    
println!("Hello, world!" /* второй комментарий */);    /* Все готово. Пока!       третий комментарий     */}

Также можно закомментировать те участки кода, которые сейчас не нужны, но в будущем потребуются.

5.6. Условные инструкции

Это аналоги логических операторов в других языках. То есть, они выполняются только в случае если некое условие или набор условий истинны. Все условные инструкции содержат проверяемую переменную и целевое значение, а оператор условия вида ==, < или > определяет их соотношение. На выходе это дает результат true («истинно») и false («ложно»), если переменная удовлетворяет или не удовлетворяет значению. В общем, обычная логическая переменная.

Существует три условных оператора: if, if else и while. Первый создает классическую пару вариантов — «если — то».

Условный оператор if выглядит так:

fn main() 
{    
let is_hot = false;  
{        
println!("It's hot!");    
}}

То есть, если условие истинно, выполняется код, если же нет — пропуск этого пункта и переход на следующую позицию.

if else — если условие истинно, выполняется тело кода A. В противном случае выполняется тело кода B. Не менее классическое ветвление.

fn main() 
{    
let is_hot = false;    
if is_hot 
{        
println!("It's hot!");    
}
 else 
{        
println!("It's not hot!");    

while: тело кода многократно выполняется, пока условие true («истинно»). Как только условие становится false («ложным»), выполнение этого цикла прекращается.

while is_raining() 
{    
println!("Hey, it's raining!");
}

При этом нужно, чтобы в циклах while проверяемая переменная была изменяемой, чтобы было условие выхода. Иначе цикл будет бесконечным.

5.7. Владение

Владение — это центральная особенность Rust и одна из причин его популярности. Речь идет о том самом механизме освобождения памяти, который есть в Rust. В тех же Java, JavaScript или Python есть сборщики мусора, которые автоматически удаляют неиспользуемые ссылки. В C или C++ от разработчиков требуется делать это вручную, что требует больше времени и создает проблемы.

В Rust же используется система владения, которая управляет памятью с помощью набора правил, применяемых компилятором во время компиляции. Для нее есть ряд правил:

  1. Каждое значение в Rust имеет переменную-владельца.
  2. В каждый конкретный момент времени у значения есть одна переменная-владелец.
  3. При выходе владельца из видимости, значение автоматически удаляется.

Это позволяет выделять память под объявленные переменные лишь пока они используются. При передаче переменных в качестве параметров в другую функцию, они копируются туда и назначаются другому владельцу. А первый лишается их.

fn main() 
{     
let x = 5; // переменная x владеет значением 5     
function(x);
} fn function (number : i32)   
{ // number становится владельцем значения 5        
let s = "memory";  // начинается область видимости переменной s, здесь s становится действительной        
// здесь с s выполняются какие-то действия    }                                  
// эта область видимости заканчивается, и теперь s больше недействительна

Главный вывод касается разного использования и x. Сначала x владеет значением 5, но после выхода ее из области видимости функции main() переменная должна передать владение параметру number. Ее использование в качестве параметра позволяет продолжить область видимости выделения памяти под значение 5 за пределы исходной функции.

С другой стороны, переменная s не используется в качестве параметра и поэтому память для нее остается выделенной только тогда, когда программа находится внутри function(). По завершении function() значение s никогда больше не потребуется и для высвобождения памяти от него избавляются.

5.8. Структуры

Это аналоги классов в Java и Python. Они обозначаются как struct. Иначе говоря, это пользовательские типы данных, создаваемые для представления типов объектов.

Синтаксис объявления структуры:

struct [identifier] {    [fieldName]: [fieldType],   [secondFieldName]: [secondFieldType],}

struct сообщает Rust, что следующее объявление определит тип данных struct.

[identifier] — это имя типа данных, используемого при передаче параметров. К примеру это string или i32 для строковых и целочисленных типов соответственно.

{} — фигурные скобки обозначают начало и конец переменных, необходимых для структуры.

[fieldName] — здесь объявляется первая переменная, которую должны иметь все экземпляры этой структуры. Переменные внутри структуры называются полями.

[fieldType] — здесь в явном виде определяется тип данных переменной.

Нижеследующий пример включает в себя структуру struct Car, в которую входит переменная строкового типа brand  и целочисленного year.

struct Car{    brand: String,    year: u16,};

Каждый создаваемый экземпляр типа Car должен иметь значения для этих полей.

Поэтому создадим экземпляр Car для конкретного автомобиля со значениями для brand (модели) и year (года выпуска).

let my_car = Car {    brand: String:: from ("BMW"), // с явно заданным строковым типом    year: 2009,};

Вот так выглядит полная структура:

fn main () 
{
struct Car
{ brand: String,    
year: u16,};
let my_car = Car 
{brand: String:: from ("BMW"),
year: 2009,};
println!("My car is a {} from {}", my_car.brand, my_car.year    );}

Как видим, структуры хорошо подходят для хранения данных, которые относятся к тому или иному типу объекта.

6. Сферы применения

Язык Rust позволяет реализовывать широкий спектр задач. В списке отметим:

  • Создание веб-серверов и клиентских приложений.
  • Программы для блокчейна.
  • Разработка ядер ОС (сейчас есть проект по переводу Linux на этот язык).
  • Программы общего назначения.
  • Различные мониторинговые системы.
  • Игры, браузеры и так далее.

Иначе говоря, Rust подходит для всех основных задач.

Итоги

Как ожидается, востребованность языка будет только расти со временем. Ведь многие компании хотят заменить им устаревшие C/C++, и это им удается.

Если же этого вводного материала мало, и вы хотите начать изучать Rust, то можно посмотреть вот эти обучающие видео:

Останні статті

Обучение Power BI – какие онлайн курсы аналитики выбрать

Сегодня мы поговорим о том, как выбрать лучшие курсы Power BI в Украине, особенно для…

13.01.2024

Work.ua назвал самые конкурентные вакансии в IТ за 2023 год

В 2023 году во всех крупнейших регионах конкуренция за вакансию выросла на 5–12%. Не исключением…

08.12.2023

Украинская IT-рекрутерка создала бесплатный трекер поиска работы

Unicorn Hunter/Talent Manager Лина Калиш создала бесплатный трекер поиска работы в Notion, систематизирующий все этапы…

07.12.2023

Mate academy отправит работников в 10-дневный оплачиваемый отпуск

Edtech-стартап Mate academy принял решение отправить своих работников в десятидневный отпуск – с 25 декабря…

07.12.2023

Переписки, фото, история браузера: киевский программист зарабатывал на шпионаже

Служба безопасности Украины задержала в Киеве 46-летнего программиста, который за деньги устанавливал шпионские программы и…

07.12.2023

Как вырасти до сеньйора? Девелопер создал популярную подборку на Github

IT-специалист Джордан Катлер создал и выложил на Github подборку разнообразных ресурсов, которые помогут достичь уровня…

07.12.2023