Ниже решил поделиться одним примером, который без проблем компилируется в Go, и не компилируется в Rust.
Есть код:
func main() {
var r *int
{
x := 5
r = &x
}
fmt.Println(*r)
}
Этот код собирается и запускается без ошибок. Что тут происходит: мы объявляем локальный блок со своей областью видимости, где объявляем переменную
x = 5, и затем копируем ссылку на нее в r.Ниже, когда блок с объявлением и копированием завершился - мы разименовываем
r и выводим значение:
➜ go run main.go
5
Давай теперь напишем тот же код на Rust:
fn main() {
let r;
{
let x = 5;
r = &x;
}
println!("{}", r);
}
Компилятор поведет себя примерно так:
➜ git:(master) ✗ cargo build
Compiling memory v0.1.0 (/Users/poltora.dev/rust/memory)
error[E0597]: `x` does not live long enough
--> src/main.rs:5:13
|
4 | let x = 5;
| - binding `x` declared here
5 | r = &x;
| ^^ borrowed value does not live long enough
6 | }
| - `x` dropped here while still borrowed
7 | println!("r: {}", r);
| - borrow later used here
For more information about this error, try `rustc --explain E0597`.
error: could not compile `memory` (bin "memory") due to 1 previous error
Ну во-первых, компилятор Rust - мое почтение 😎. Очень подробный анализ ошибок (но это неспроста, ниже объяснение - почему).
А во-вторых, почему мы получили ошибку?
Все дело в базовой концепции работы Go и Rust с памятью. В Go компилятор имеет фазу escape-анализа, которая проверяет код и помечает, на стек или на кучу должна упасть та или иная переменная. Когда компилятор видит, что переменная r принимает значение из локальной области видимости (x) - он перемещает ее на кучу - в общую память процесса, с которой могут работать все потоки.
Затем, в процессе работы программы, параллельно с Go вступает в игру Garbage Collector, который дает нам гарантию, что все такие переменные, даже если мы про них забудем и не удалим их (а мы обычно этого не делаем) - будут освобождены. Скорость разработки в обмен на скорость работы программы.
У Rust нет Garbage Collector, но есть несколько концепций, которые гарантируют, что утечек памяти не будет, и одна из них - это фаза компиляции под названием анализ времени жизни. Именно он видит, что r принимает значение от объекта, время жизни которого закончится раньше, чем будет выведена r - и ругается. В случае с Rust компилятор заставляет разработчика больше думать о коде, но не запускает Garbage Collector. Сложность разработки в обмен на скорость исполнения.
dev notes | golang digest