4 ms·
Can someone explain to me (like a five yo) how the borrow checker is different to Go's -race flag for compilation? Genuine question.
by awesome_dude 7y ago
Can someone explain to me (like a five yo) how the borrow checker is different to Go's -race flag for compilation?
Genuine question.
- TheDong 7y agoAbsolutely. The borrow checker in rust has no runtime overhead (whereas go's race flag is runtime only, and has so much overhead you can't turn it on in prod). In addition, the borrow checker catches data races, but it also catches more subtle ownership issues too! For one example of an ownership issue which isn't also a concurrent data-race, let's look at something in go that the -race flag doesn't catch, but that the borrowchecker in rust does: // go b := []byte("foo bar") foo := b[0:3] fmt.Println(string(foo)) b = b[:0] b = append(b, []byte("not foo")...) fmt.Println(string(foo)) // prints "not" // This is because 'foo' is actually "borrowing" // bytes from b, but go doesn't have any way to indicate // that said "borrow" is active, so writing to it might be a bug. That code is here: https://play.golang.org/p/0EK2OzeUG0v https://play.golang.org/p/0EK2OzeUG0v Now let's try to write code that has that same bug in rust: let mut b = "foo bar".as_bytes().to_vec(); let foo = &b[0..3]; println!("{}", String::from_utf8_lossy(foo)); b.truncate(0); b.append(&mut "not foo".as_bytes().to_vec()); println!("{}", String::from_utf8_lossy(foo)); That code is here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8014cbe265ef560048b0315327ca5138 https://play.rust-lang.org/?version=stable&mode=debug&editio... If you try to compile it, you'll see that it doesn't compile with the following error: "error[E0502]: cannot borrow `b` as mutable because it is also borrowed as immutable" In the rust code, the borrow checker keeps track of the fact that "foo" is currently a reference into "b", and because of that, it knows that it shouldn't let the programmer modify the memory out from under "foo". This is just one of many examples I could give, but hopefully it provides some explanation of how the borrow checker is more flexible than just race detection. Basically, there's a lot of cases in programming when it's useful to have a function which takes ownership of an object and turns it into something else, or borrows an object temporarily but then gives back ownership. In languages like go, the fact that "close(channel)" means that the channel can't be written to again is a runtime guarantee. In rust, it can be expressed to the compiler that 'channel.close()' means that channel is gone and using it again is a compile time error. Ownership ends up being a really common concern, and the borrow checker handles it nicely.
- zelly 7y ago-race detects data races at runtime. Rust's borrow checker works at compile time. Borrow checking prevents data races by making it impossible for two threads to have read/write access to the same memory. (But there are many ways around it.)