Rust vs Go: практическое сравнение для настоящих разработчиков
Вечный спор, Rust против Go, это как Cola против Pepsi, Mac против PC или — если вы действительно придерживаетесь старой школы — Java против C++. Оба современные, блестящие и готовы решить ваши проблемы с программированием, но они не могут быть более разными. Какой из них лучше? Ну, это зависит от обстоятельств. В этой статье мы разберем это по частям.
Производительность
Rust: Представьте, что вы создаете собственный игровой движок, и каждая наносекунда имеет значение. Отсутствие в Rust сборщика мусора и его лазерно-сфокусированный контроль над памятью делают его невероятно быстрым.
fn calculate_fibonacci(n: u32) -> u32 {
match n {
0 => 0,
1 => 1,
_ => calculate_fibonacci(n - 1) + calculate_fibonacci(n - 2),
}
}
Посмотрите на это. Он настолько быстрый и оптимизированный, что вы можете запустить его на картофелине и все равно получить результаты за миллисекунды. Но будьте осторожны — если вы не будете правильно обращаться с владением, компилятор будет выдавать вам ошибки, пока вы не сделаете все правильно.
Go: Теперь давайте перепишем это на Go:
func calculateFibonacci(n int) int {
if n <= 1 {
return n
}
return calculateFibonacci(n-1) + calculateFibonacci(n-2)
}
Конечно, это немного медленнее, потому что в Go есть сборщик мусора, но знаете что? Вам вообще не нужно было думать о памяти. Для 99% веб-приложений это победа.
Безопасность памяти
Rust: Rust относится к безопасности памяти настолько серьезно, что создается впечатление, будто на вас кричит сержант-инструктор по строевой подготовке:
fn main() {
let x = vec![1, 2, 3];
let y = x; // Ownership transferred
println!("{:?}", x); // Error: x was moved!
}
Да, поначалу это сбивает с толку, но это не даст вам выстрелить себе в ногу.
Go: Между тем, Go говорит: «Успокойся, я понял». Его сборщик мусора обрабатывает память, поэтому вам не нужно беспокоиться о владельце или указателях.
package main
import "fmt"
func main() {
x := []int{1, 2, 3}
y := x // This works fine
fmt.Println(x)
}
Go делает вещи простыми, но ценой тонкого контроля. Если вы создаете видеоигру или операционную систему, это может не подойти.
Параллелизм
А вот с параллелизмом дела обстоят сложнее.
Rust: Модель параллелизма Rust мощная, но ощущается как сборка кубика Рубика вслепую. Вот пример с потоками:
use std::thread;
fn main() {
let handles: Vec<_> = (0..10).map(|i| {
thread::spawn(move || {
println!("Thread {} is running", i);
})
}).collect();
for handle in handles {
handle.join().unwrap();
}
}
Круто, правда? Но Rust обеспечивает безопасность с помощью своей модели владения, так что если вы ошибетесь, компилятор даже не позволит вам запустить код.
Go: Go, с другой стороны, упрощает параллелизм с помощью горутин:
package main
import (
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Printf("Goroutine %d is running\n", i)
}(i)
}
wg.Wait()
}
Никаких сложных моделей или кошмаров с владением — просто запускайте горутины и позвольте среде выполнения Go позаботиться обо всем остальном.
Кривая обучения
Rust: Изучение Rust похоже на изучение исчисления в старшей школе — это сложно, но как только вы это поймете, вы почувствуете себя гением. Такие концепции, как время жизни, владение и заимствования, поначалу смутят вас, но они гарантируют, что ваш код будет прочным как скала.
Go: Go похож на книги "Learn to Code in 7 Days". Его простота означает, что вы можете быстро стать продуктивным:
package main
import "fmt"
func main() {
fmt.Println("Hello, world!")
}
Никаких шаблонов, никаких криптографических сообщений об ошибках — просто пишите и запускайте. Идеально подходит для новичков или команд, которым нужны быстрые результаты.
Экосистема
Rust: Экосистема Rust быстро растет. Такие инструменты, как cargo
делают легким управление зависимостями, а библиотеки на crates.io
охватывают все: от веб-фреймворков до криптографии.
Пример: Нужно асинхронное программирование? В Rust вам поможет tokio
:
use tokio::time::sleep;
use std::time::Duration;
#[tokio::main]
async fn main() {
sleep(Duration::from_secs(1)).await;
println!("Hello, async world!");
}
Go: Экосистема Go зрелая, особенно для веб и облачной разработки. Такие фреймворки, как gin
и инструменты, как docker
, делают Go фаворитом для DevOps и бэкэнд-сервисов.
Пример: Простой веб-сервер с net/http
:
package main
import (
"fmt"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello, world!")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":8080", nil)
}
Вы можете создать и развернуть это за считанные минуты.
Реальные примеры использования
- Rust: Создание игрового движка? Написание операционной системы? Создание высокопроизводительных инструментов, таких как
ripgrep
? Rust — это то, что вам нужно. - Go: Нужно создать масштабируемый API? Написание облачных инструментов, таких как Kubernetes? Автоматизация конвейера CI/CD? Пройдите весь путь.
Заключительные мысли
- Rust: Для перфекционистов, любителей контроля и фанатов производительности. Это сложно, но награда того стоит.
- Go: Для прагматиков, быстрых и командных игроков. Просто, эффективно и идеально подходит для веб-приложений.