Перейти к содержимому

Rust против C++, Go и Python

«Учить Rust, или Go, или остаться на Python?» — один из самых частых вопросов у тех, кто только узнал о Rust. Честный ответ: зависит от того, что ты хочешь создавать. Все языки на этой странице хорошие, ими пользуются миллионы людей. Просто каждый из них сделал свои компромиссы.

Представь языки как транспорт. Спорткар, фургон и велосипед — все «хорошие». Просто на велосипеде не перевозят мебель. Эта страница простыми словами объясняет компромиссы, чтобы ты мог выбрать подходящий инструмент для своей задачи. Мы постараемся быть честными, в том числе насчёт слабых мест Rust.

Главное обещание Rust — скорость и безопасность одновременно:

  • Быстрый, как C и C++. Rust компилируется в машинный код и не использует сборщик мусора, поэтому нет пауз на очистку памяти.
  • Безопасная работа с памятью без сборщика мусора. Правила владения и заимствования проверяются при компиляции. Целые группы ошибок (использование освобождённой памяти, гонки данных между потоками) отвергаются ещё до запуска программы.
  • Отличные инструменты из коробки. Один инструмент, Cargo, собирает код, скачивает библиотеки, запускает тесты и генерирует документацию. Форматтер (rustfmt) и линтер (clippy) — официальные.
  • Полезные сообщения об ошибках. Компилятор часто прямо говорит, как исправить проблему.

И разработчикам он, похоже, нравится: в опросе разработчиков Stack Overflow 2025 года (на английском) Rust снова стал «самым любимым» языком — то есть большинство тех, кто на нём пишет, хотят продолжать.

Начнём с простого: сложим чётные числа из списка. Вот так это выглядит на Rust:

fn main() {
let numbers = vec![1, 2, 3, 4, 5, 6];
let sum: i32 = numbers.iter().filter(|n| *n % 2 == 0).sum();
println!("Сумма чётных чисел: {sum}");
}

Вывод:

Сумма чётных чисел: 12
▶ Попробуй сам

Та же программа на C++:

#include <iostream>
#include <vector>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6};
int sum = 0;
for (int n : numbers) {
if (n % 2 == 0) {
sum += n;
}
}
std::cout << "Сумма чётных чисел: " << sum << "\n";
}

На Go:

package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5, 6}
sum := 0
for _, n := range numbers {
if n%2 == 0 {
sum += n
}
}
fmt.Println("Сумма чётных чисел:", sum)
}

И на Python:

numbers = [1, 2, 3, 4, 5, 6]
total = sum(n for n in numbers if n % 2 == 0)
print("Сумма чётных чисел:", total)

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

C++ и Rust нацелены на одну и ту же область: быстрые программы с полным контролем над памятью — браузеры, игровые движки, базы данных и операционные системы. Главное различие в том, кто проверяет, что память используется правильно.

В C++ это делаешь ты. Вот классическая ошибка. Компилятор C++ примет этот код без единой жалобы, но push_back может перенести вектор в новую область памяти, и тогда first будет указывать на уже освобождённую память:

std::vector<int> v = {1, 2, 3};
int& first = v[0];
v.push_back(4); // может перенести память вектора...
std::cout << first << "\n"; // ...и тогда здесь читается освобождённая память

Та же ошибка в Rust не скомпилируется:

fn main() {
let mut v = vec![1, 2, 3];
let first = &v[0];
v.push(4);
println!("{first}");
}
error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable
--> src/main.rs:4:5
|
3 | let first = &v[0];
| - immutable borrow occurs here
4 | v.push(4);
| ^^^^^^^^^ mutable borrow occurs here
5 | println!("{first}");
| ----- immutable borrow later used here

Компилятор говорит: нельзя заимствовать v как изменяемое, потому что оно уже заимствовано как неизменяемое, и это неизменяемое заимствование используется дальше.

На практике это очень важно. Команда Chromium сообщает, что около 70% их серьёзных уязвимостей — это проблемы безопасности памяти (на английском). Команда Android в Google уже несколько лет пишет новый код на Rust. В 2022 году они сообщили, что в коде Android на Rust не найдено ни одной уязвимости, связанной с памятью (на английском), а в 2025 году — что плотность уязвимостей памяти в нём более чем в 1000 раз ниже, чем в их коде на C и C++ (на английском). Ядро Linux (на английском) тоже поддерживает код на Rust.

Инструменты: у C++ много систем сборки и пакетных менеджеров, и команды часто тратят заметное время на настройку. У Rust есть один стандартный инструмент — Cargo, поэтому любой проект на Rust собирается одинаково.

Порог входа: оба языка сложные. C++ огромен, и многие его правила узнаёшь, совершая ошибки во время работы программы. Правила Rust строгие, но компилятор следит за ними и объясняет их, поэтому ты учишь их на этапе компиляции.

Где C++ всё ещё выигрывает: десятилетия существующего кода, библиотеки и опытные разработчики. Если твоя компания или твой игровой движок построены на C++, то C++ часто будет практичным выбором. Rust умеет вызывать код на C, а C++ — через интерфейсы в стиле C, но совмещать их требует усилий.

Go и Rust часто сравнивают: оба современные, компилируемые и популярные для серверов и консольных утилит. Но их создавали с разными целями.

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

Многопоточность. Горутины и каналы Go знамениты своей простотой. Вот небольшой пример, который параллельно возводит в квадрат три числа и складывает результаты:

results := make(chan int)
for n := 1; n <= 3; n++ {
go func() {
results <- n * n
}()
}
total := 0
for i := 0; i < 3; i++ {
total += <-results
}
fmt.Println("итого:", total)

Версия на Rust использует потоки и канал. Она чуть многословнее: для каждого потока нужно клонировать отправителя и использовать move:

use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
for n in 1..=3 {
let tx = tx.clone();
thread::spawn(move || {
tx.send(n * n).unwrap();
});
}
drop(tx); // закрываем свою копию, чтобы цикл ниже мог завершиться
let total: i32 = rx.iter().sum();
println!("итого: {total}");
}

Вывод:

итого: 14
▶ Попробуй сам

Что ты получаешь за лишние строчки: Rust отказывается компилировать код, в котором два потока могут одновременно менять одни и те же данные (гонка данных, data race). В Go гонки данных возможны; для их поиска есть детектор гонок (на английском), который находит их во время работы программы. Подробнее — в уроке Многопоточность.

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

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

Python и Rust очень разные, и их часто используют вместе, а не вместо друг друга.

Скорость. Python интерпретируемый, поэтому чистый код на Python, который много вычисляет, намного медленнее компилируемого. Именно поэтому популярные библиотеки для Python часто делают тяжёлую работу на C, C++ или Rust «под капотом».

Типизация. В Python типы проверяются во время работы программы. Эта функция работает с числом и падает со строкой, но узнаёшь ты об этом, только когда выполнится эта строка:

def add_tax(price):
return price * 1.2
print(add_tax(10)) # 12.0
print(add_tax("10")) # TypeError: can't multiply sequence by non-int of type 'float'

В Python есть необязательные подсказки типов, но среда выполнения Python их не проверяет (на английском). В Rust каждый тип проверяется до запуска программы:

fn add_tax(price: f64) -> f64 {
price * 1.2
}
fn main() {
println!("{}", add_tax(10.0));
println!("{}", add_tax("10"));
}
error[E0308]: mismatched types
--> src/main.rs:7:28
|
7 | println!("{}", add_tax("10"));
| ------- ^^^^ expected `f64`, found `&str`
| |
| arguments to this function are incorrect

Компилятор говорит о несовпадении типов: ожидался f64, а передана строка &str.

Когда Python всё ещё лучше: быстрые скрипты, анализ данных, машинное обучение, ноутбуки, автоматизация и обучение новичков. Экосистема Python для data science огромна, и идею можно проверить за минуты. Если программа большую часть времени ждёт сеть или базу данных, скорость Python часто не важна.

Используем оба: проект PyO3 (на английском) позволяет писать модули для Python на Rust, так что можно оставить свой код на Python и перенести в Rust только медленную часть. Некоторые популярные инструменты для Python сами написаны на Rust; например, в опросе Stack Overflow отмечено, что пакетный менеджер uv для Python написан на Rust (на английском).

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

Rust C++ Go Python
Скорость Очень высокая, машинный код, без GC Очень высокая, машинный код, без GC Высокая, машинный код со сборщиком мусора Ниже при тяжёлых вычислениях на чистом Python
Безопасность памяти Проверяется при компиляции (вне unsafe) Ответственность программиста GC; гонки данных всё ещё возможны GC; безопасно, но ошибки типов всплывают во время работы
Порог входа Поначалу крутой Крутой, и язык очень большой Пологий Самый пологий
Время компиляции Довольно долгое Довольно долгое (зависит от проекта) Очень быстрое Компиляции нет
Экосистема Моложе, растёт; Cargo встроен Огромная и зрелая; много инструментов сборки Сильная стандартная библиотека, отлично для серверов Огромная, особенно для данных и ML
Типичные применения CLI-утилиты, серверы, системный код, WebAssembly, встраиваемые системы Игры, браузеры, высокопроизводительные и унаследованные системы Веб-сервисы, облачные и DevOps-инструменты Скрипты, data science, ML, автоматизация

Задай себе такие вопросы:

  • Нужна максимальная скорость или малое потребление памяти, например игровой движок, база данных или код на маленьком устройстве? Rust (или C++) — сильный выбор.
  • Ошибки будут дорогими? Код, критичный для безопасности, долго работающие серверы и код, от которого зависят многие люди, больше всего выигрывают от проверок Rust при компиляции.
  • Делаешь консольную утилиту, которой хочешь поделиться? Rust выдаёт один быстрый исполняемый файл, без среды выполнения, которую нужно устанавливать. Go здесь тоже отлично подходит.
  • Нужен веб-сервис побыстрее, а команда новичок во всех этих языках? С Go вы, скорее всего, доберётесь до цели быстрее. Rust тоже хорошо подходит (см. Проект: веб-API на Axum), но на обучение уйдёт больше времени.
  • Это быстрый скрипт, анализ данных или эксперимент с ML? Бери Python. К Rust можно обратиться позже, через PyO3, если какая-то часть окажется слишком медленной.
  • Есть большая существующая кодовая база на C++? Пока оставайся на C++ или добавляй Rust в новые, отдельные части, как это сделали в Android.

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

Rust не идеален. Вот с чем люди мучаются чаще всего:

  • Порог входа реален. Первые недели с анализатором заимствований (borrow checker) могут раздражать. Приготовься какое-то время «бороться с компилятором». Как только владение уляжется в голове, станет намного легче.
  • Время компиляции больше, чем у Go, особенно на больших проектах и в релизных сборках.
  • Некоторые вещи выразить сложнее. Данные в виде графа с множеством связей между объектами или код, который активно разделяет изменяемое состояние, требуют инструментов вроде умных указателей и более тщательного планирования.
  • Экосистема моложе. В некоторых областях (GUI-приложения, отдельные научные библиотеки) зрелых вариантов меньше, чем в C++ или Python.
  • Им пользуется меньше людей (пока). В опросе Stack Overflow 2025 года (на английском) гораздо больше участников сказали, что используют Python (около 58%) или C++ (около 24%), чем Rust (около 15%). Значит, меньше опытных разработчиков на Rust, которых можно нанять, у которых можно спросить или поучиться на работе.
  • Больше кода для простых вещей. Как показал пример с каналом, Rust часто просит явности там, где другие языки расслаблены.
  • Rust даёт скорость уровня C++ с безопасностью памяти, которая проверяется при компиляции, и отличные встроенные инструменты.
  • По сравнению с C++ Rust предотвращает целые классы ошибок памяти до запуска программы; у C++ гораздо больше существующего кода и библиотек.
  • По сравнению с Go в Rust нет сборщика мусора, а гонки данных останавливаются при компиляции; Go проще и компилируется намного быстрее.
  • По сравнению с Python Rust намного быстрее и проверяет типы до запуска; на Python быстрее писать, и он доминирует в data science. PyO3 позволяет их совмещать.
  • Главная цена Rust — крутой порог входа, более медленная компиляция и более молодая экосистема.

Проверь себя

1. Как Rust управляет памятью без сборщика мусора?

2. Какое из этих преимуществ Go перед Rust справедливо?

3. В программе на Python одна функция работает слишком медленно. Какой подход распространён?

4. Что происходит в примере C++ против Rust с кодом, который хранит &v[0], а потом добавляет элемент в v?

Результат: 0 / 4