How does Rust guarantee memory safety and prevent segfaults?
Asked Answered
F

1

22

I was looking for a language to learn, and I saw that Rust is getting quite popular.

Two things impressed me about Rust, memory safety and preventing segfaults.

How does Rust achieve this? What differences between Rust and Java for example enable Rust's safety features?

Filicide answered 21/3, 2016 at 16:11 Comment(4)
Java also guarantees memory safety and prevents segfault (through its VM + Garbage Collector), so what difference between Java and Rust are you looking for? Also, have you read the Rust Book? (it might not answer your questions). Also, have you read #29629150 ?Speechmaker
Java uses a tracing garbage collector that keeps all objects alive except those that are provably unreachable (and therefore their absence is unobservable). Rust uses an affine type system that ensures that each object has a well-defined lifetime and rules that forbid taking references that could survive past the lifetime of the referent object.Domenic
As far as I know, Java does not guarantee memory safety.Justiceship
@Saibot: What does memory safety mean, to you :) ?Speechmaker
S
52

How Rust achieves memory safety is, at its core, actually quite simple. It hinges mainly on two principles: ownership and borrowing.

Ownership

The compiler uses an affine type system to track the ownership of each value: a value can only be used at most once, after which the compiler refuses to use it again.

fn main() {
    let original = "Hello, World!".to_string();
    let other = original;
    println!("{}", original);
}

yields an error:

error[E0382]: use of moved value: `original`
 --> src/main.rs:4:20
  |
3 |     let other = original;
  |         ----- value moved here
4 |     println!("{}", original);
  |                    ^^^^^^^^ value used here after move
  |
  = note: move occurs because `original` has type `std::string::String`, which does not implement the `Copy` trait

This, notably, prevents the dreaded double-free regularly encountered in C or C++ (prior to smart pointers).

Borrowing

The illumination that comes from Rust is that memory issues occur when one mixes aliasing and mutability: that is, when a single piece of memory is accessible through multiple paths and it is mutated (or moved away) leaving behind dangling pointers.

The core tenet of borrow checking is therefore: Mutability XOR Aliasing. It's similar to a Read-Write Lock, in principle.

This means that the Rust compiler tracks aliasing information, for which it uses the lifetime annotations (those 'a in &'a var) to connect the lifetime of references and the value they refer to together.

A value is borrowed if someone has a reference to it or INTO it (for example, a reference to a field of a struct or to an element of a collection). A borrowed value cannot be moved.

Mutability (without aliasing)

You can obtain only a single mutable reference (&mut T) into a given value at any time, and no immutable reference into this value may exist at the same time; it guarantees that you have exclusive access to this tidbit of memory and thus you can safely mutate it.

Aliasing (without mutability)

You can obtain multiple immutable references (&T) into a given value at any time. However you cannot mutate anything through those references (*).

(*) I am lying; there are structs like RefCell which implement "interior mutability"; they do respect the Mutability XOR Aliasing principle, but defer the check to run-time instead.

That's it?

Nearly so ;)

It is already quite complicated to implement for the compiler-writers, and may unduly constrain the users (some programs that would be safe cannot be proven safe using this system, requiring to jump through hoops), however the core principles are really that simple.

So what's left?

Bounds-checking. It is not rocket-science, but may induce a performance penalty though. Most languages have some degree of support for it, C being the big exception, and C++ having some support for it, although it is optional.

Speechmaker answered 21/3, 2016 at 17:4 Comment(7)
Thanks for answer. Would you like to add something about segfault prevention?Filicide
@Myra: A segmentation fault is only one of the symptoms of an unsafe memory access. Rust is memory safe, thanks to the mechanism explained here, and therefore is as free of segmentation fault as it is free of memory corruptions.Speechmaker
@Filicide With rust only segfault you easily can provoke is "stack overflow". Any other segfaults mean there is bug in the compiler or in libraries that uses unsafe code (and C libraries through ffi).Brause
@qthree: Actually, a small "stack overflow" (< 4KB) is no longer reported as a segmentation fault since the latest release to simplify the message since many people equate "memory safe" with "segmentation fault free" not realizing that not all segmentation faults are a memory safety issue. I believe there's an open bug to implement proper stack-depth checking to ensure that even large stack allocations will not cause a stack overflow.Speechmaker
Your answer and most Rust propaganda seems to focus solely on temporal memory safety, i.e. preventing double frees and use after frees, where Rust has indeed an interesting solution. What about spatial memory safety though? How are out of bounds memory accesses prevented?Bunnybunow
@CatalinHritcu: Propaganda, really? :D The focus is mostly on temporal safety because it is where Rust innovates. On spatial safety, Rust simply performs bounds-check at run-time, like pretty much any other language. There have been library experiments on branded indices, but without direct language support the "solutions" are awkward.Speechmaker
Dynamic bounds checks make sense as an answer here, but this is not what "pretty much any other language" does. Memory unsafe languages like C/C++ don't do these checks at their programmers' peril. Given that spatial memory safety is as important for security as the temporal kind, I suggest updating your answer to reflect this reality, instead of making it sound as if temporal memory safety is "all there is to it". I believe Rust is awesome enough that it doesn't need propaganda that silently redefines general concepts like "memory safety" to mean only "temporal memory safety".Bunnybunow

© 2022 - 2024 — McMap. All rights reserved.