Python — самый популярный язык программирования в мире, который используется почти во всех сферах. Он универсальный, удобный в использовании, и его легко выучить. Но из-за универсальности и удобности скорость некоторых сложных программ в Python получается невысокая, и из-за этого приходится подключать модули на других языках.
Rust — молодой язык программирования, который сложнее в изучении, зато код на нём работает быстрее. Если нужно ускорить программу, написанную на Python, можно написать часть кода на Rust и запускать эту часть из Python-скрипта.
Сегодня разберём, для каких задач ещё подходит Rust, установим его на компьютер и посмотрим, как он вообще выглядит для человека, знакомого с Python.
Что такое Rust
Rust начал разрабатывать один из разработчиков Mozilla Грэйдон Хорр в 2006 году как личный проект, а в 2009 Mozilla начала официально спонсировать проект. Первая готовая для массового использования версия появилась в 2015 году.
Вместо кардинально новых функций Хорр сосредоточился на безопасности языка: собирал уже работающие методы и возможности и делал их работу надёжнее и быстрее. Это отражено в названии языка, который назван в честь ржавчинных грибов (rust по-английски — «ржавый»), которые очень живучие и быстро растут.
Сейчас это производительный, обеспечивающий безопасность памяти язык, которой защищает код от целого ряда ошибок, которые может совершить программист. У Rust понятный синтаксис в котором легко разобраться, встроенный менеджер пакетов, и код на нём легко поддерживать и исправлять благодаря понятным сообщениям об ошибках.
Получается, что Rust не делает ничего нового, зато в нём нет недостатков других языков:
- он быстрее простых универсальных языков вроде Python или Ruby, хотя сложнее;
- Rust, как и Python, защищает код от ошибок — но при этом не ограничивает возможности программы;
- Rust безопаснее C или C++, при этом часто почти такой же по скорости.
Кто использует Rust
Rust — мощный и безопасный язык, который может стать хорошей заменой C++. Конечно, переписывать огромные базы кода на другом языке долго и дорого, но многие компании делают это постепенно: например, основная часть Windows написана на C, но разработчики уже переписывают самые уязвимые части кода на Rust.
Вот отрасли и компании, которые используют Rust или внедрили его для некоторых фрагментов:
- Облачная инфраструктура и бэкенд-технологии (AWS, Google).
- Web3 — идея интернета на основе блокчейна (Near, Ethereum).
- Операционные системы (Linux, Windows, Android).
- Браузеры (Mozilla Firefox).
- Базы данных (Surreal DB).
- Интернет вещей (Samsung).
- Кибербезопасность (Microsoft, 1Password).
- Финансы (Two Sigma, Kraken).
- Машинное обучение и Data Science (Grok, polars).
- Авиационное и космическое производство (NASA, Cryptosat и Airhart).
- Роботостроение (Scythe Robotis, Tangram Visions).
- Разработка игр (Embark Studios и Ready at Dawn).
Запуск кода на Python
Отличие Rust от Python в том, что Python интерпретируемый язык, а Rust — компилируемый. Сейчас поясним, что это значит.
На Python, достаточно написать код и его сразу можно запускать. Например:
print('Привет, Python-код!')
Для запуска можно нажать специальную кнопку в среде разработки IDE или ввести такую команду в терминале, находясь в папке проекта, — просто пишем название файла со скриптом:
python hello_world.py
В консоли получим:
Установка и запуск кода на Rust
Rust — это компилируемый язык: написанный код компьютеру нужно сначала проверить, а потом перевести это на язык машинных команд. Для запуска кода нужно сначала установить Rust, например такой командой в терминале:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Проверить установку можно так:
rustc --version
Если всё в порядке, в консоли мы увидим номер установленной версии компилятора Rust. После этого можно написать первый код и сохранить его как hello_world.rs:
fn main() {
println!("Привет, первый Rust-код!");
}
👉 Это выглядит немного сложнее, чем то же самое Python, но если вы знаете хотя бы основы Python, то легко сможете понять, что тут происходит.
Запустить Rust-код одной командой или кнопкой нельзя, сначала его нужно скомпилировать. Самый простой способ сделать это на Rust — воспользоваться встроенным менеджером пакетов Cargo, который применяется в большинстве задач на этом языке.
Допустим, если мы хотим выполнить код выше, нужно сначала создать проект. Чтобы не создавать все нужные для этого файлы вручную, открываем терминал, переходим в каталог с нашим скриптом и пишем такое:
cargo new hello_world
В том каталоге, где выполнена команда, появится папка hello_world. В ней будет отдельная папка с нашим скриптом на Rust и файл-манифест с инструкциями Cargo.toml на языке TOML, который необходим для компиляции.
Чтобы скомпилировать код, нужно перейти в папку hello_world и в терминале выполнить команду:
cargo build
Внутри проекта появится ещё одна папка — target\debug, в который будет лежать исполняемый файл hello_world. На Windows-системах у него будет расширение .exe, а на MacOS или Linux у таких файлов вообще нет расширения — просто hello_world.
Чтобы запустить код, нужна такая команда:
cargo run
В консоли мы увидим, что файл сначала компилируется, потом запускается. После этого получаем результат работы написанного кода:
❗️ Запускать код на Rust дольше, чем на Python, но неудобно только при первом знакомстве. Зато код защищён от большого количества ошибок и работает быстрее.
Типизация данных в Python
Чтобы лучше понять типизацию в Rust, сначала посмотрим, как это сделано в Python.
Python — динамически типизируемый язык. Любой переменной можно присваивать любое значение: строку, целое число или число с плавающей запятой, список, словарь, кортеж. После присваивания мы можем заново установить значение: сначала в переменной была строка, в середине программы мы положили туда число, а к концу выполнения — словарь.
Переприсваивание может привести к беспорядку, поэтому хорошим тоном при разработке на Python являются подсказки к переменным, которые называются аннотациями типов. После каждой переменной в программе разработчик подписывает, что за тип данных может в ней находиться, и этот тип данных не меняется.
Аннотации типов выглядят так:
# тип данных для переменной x - целое число
# присваиваем переменной значение 1000
x: int = 1000
# тип данных для переменной y - строка
# присваиваем переменной значение 'Привет, КОД'
y: str = 'Привет, КОД!'
# тип данных для переменной z - словарь
# объявляем переменную и пока не присваиваем ей значение
z: dict
Аннотации типов ни к чему не обязывают. Даже с ними можно хранить в переменных любые данные и менять их за программу любое количество раз, это не будет ошибкой.
Типизация данных в Rust
Rust — статически типизируемый язык. Это значит, что память на все переменные выделяется на этапе компиляции, поэтому программа должна понимать, что хранится в каждой переменной.
В некоторых статически типизируемых языках, таких как C, C++ или Java, для этого нужно указывать обязательные аннотации типов к каждой переменной. В Rust так тоже можно. Типы данных указываются так же, как в Python — через двоеточие:
// тип переменной - строка
let alphabet: char = 'A';
// тип переменной - целое число, которое занимает в памяти 32 бита
let number: i32 = 200;
// переменная булевого типа со значением true
let flag1: bool = true;
Но такие аннотации в Rust указывать необязательно, потому что он сам понимает, что за тип данных соответствует присвоенному значению.
Например, в этой переменной Rust сам увидит целое 8-битное число типа u8 и запросит нужное количество оперативной памяти:
let elem = 5u8;
Ещё переменные в Rust можно изменять, если присвоить им тип mut
. Такой код выполнится без ошибок:
// Изменяем переменную типа mut
let mut a = 5;
a = 6;
Python-разработчикам такой подход покажется удобным и знакомым: аннотации писать необязательно, а переменные можно переприсваивать — правда, только в пределах своего типа. Нельзя объявить переменную let mut a = 5
и потом записать в неё строку.
В итоге код получается аккуратнее и заставляет продумывать ход программы, чтобы использовать типы данных с минимальной загрузкой оперативной памяти.
Классы Python и структуры Rust
Python
В программировании есть две основных парадигмы: объектно-ориентированное программирование (ООП) и функциональное программирование.
Python работает по принципам объектно-ориентированного программирования. Мы можем создать класс — шаблон кода — и задать для него несколько способов, которыми этот класс можно использовать. Эти способы называются методы.
Например, можно создать класс User и описать для него методы работы с именем и адресом электронной почты:
# объявляем класс
class User:
# настраиваем метод при создании объекта класса
def __init__(self, name):
# при создании нужно будет задать имя
self.name = name
# почта создастся автоматически с использованием имени и шаблона
self.email = name + '@mail.code.ru'
# настраиваем второй метод, при вызове нужно
# задать сообщение для аргумента message
def send_email(self, message):
# при вызове метод выводит строку с сообщением
print(f'На {self.email} отправлено {message}')
Ещё мы можем на основе первого класса создать другой класс — тогда во втором классе будут доступны те же методы.
Rust
Rust — полностью функциональный язык, классов и наследования в нём нет.
Сложные типы данных создаются через структуры — группы данных, которые потом можно использовать по отдельности. Это может выглядеть так:
// создаём структуру User c двумя методами-полями:
struct User {
name: String,
email: String
}
Чтобы пользоваться методами, их нужно создать отдельно. Это делается через ключевое слово impl
и указание названия структуры:
// реализуем методы для структуры
impl User {
// метод new создаёт новый экземпляр User
// он принимает ссылку на строку &str и возвращает объект типа User
fn new(name: &str) -> User {
// прописываем тело функции
User {
// конвертируем строку name из типа &str в String
name: name.to_string(),
// создаём строку с форматом email
email: format!("{}@mail.code.ru", name)
}
}
}
Вместе готовыми методами можно писать основную функцию — при запуске скрипта запустится именно она:
// основная функция, которая запускается при старте программы
fn main() {
// создаём переменную user и присваиваем ей объект структуры User
let user = User::new("Игорь");
// выводим на экран приветствие с именем пользователя
println!("Привет, {}!", user.name);
// выводим email пользователя
println!("Ваша электронная почта: {}", user.email);
}
Как совместить плюсы обоих языков
Многие библиотеки Python написаны на других языках, а через Python их можно использовать с привычным удобным синтаксисом. Так работают библиотеки для обработки больших данных, например NumPy или SciPy.
На Rust библиотеки для Python тоже есть, например polars
— ещё одно расширение для работы со сложными высоконагруженными операциями.
Можно написать своё расширение для Python на Rust со своими функциями — для этого есть специальная библиотека PyO3. Она позволяет совместить преимущества обоих языков. Это интересная, но сложная тема, и про работу с PyO3 подробно расскажем в отдельной статье. Если кратко:
- Rust позволяет писать безопасный код, который может выполнять несколько операций одновременно;
- поэтому сначала мы пишем модуль на Rust и создаём быстрые функции;
- после этого из Python-кода можно вызвать этот модуль и пользоваться преимуществами Rust как обычной библиотекой.
Нужен ли Rust, если есть Python
Если программы на Python выполняют свои задачи, работают без частых поломок и не предназначены для высоких нагрузок и большого количества пользователей — не нужен.
С другой стороны, Rust активно развивается, его функции можно добавлять в Python, а ещё для Python-разработчика учить его будет уже не так сложно, как начинать с нуля. Поэтому, если Python вам мало — точно стоит попробовать Rust.