5 ms·
If you've never written a line of Rust in your life, then your position could be more informed. I've written a lot of Rust, and see how much one sacrifices to g
by verdagon 4y ago
If you've never written a line of Rust in your life, then your position could be more informed. I've written a lot of Rust, and see how much one sacrifices to get the memory safety guarantees without losing speed or memory safety. You can't make basic observers, RAII, dependency injection, back-references, or a lot of other useful patterns, without sacrificing speed or safety.
- staticassertion 4y agoI'm experienced in security and rust and I agree with mjg. I've probably written 100s of thousands of lines of Rust, and I've been in security for well over a decade. None of what you said is even true, all of those patterns are trivial, except "back-references" which are very slightly non-trivial. And none of it is relevant. We don't need another memory unsafe language. It's fine if it's a toy, but this obviously isn't. It causes real harm.
- verdagon 4y agoEverything I said is true. They all rely on having a mutable member reference to affect the outside world, which the borrow checker rejects. You can try to make it happen with a generic lifetime parameter for your structs, but you end up making invisible the thing you're pointing at, making it useless. One can use unsafe to have shared mutability, or sacrifice speed with Rc/RefCell (increments/decrements) or Cell (copying, especially bad when copying Vecs which causes heap allocations). Basic observers aren't possible. The closest thing we can get is some sort of modified observer-like substance that returns a command, which requires a lot of wiring and incidental complexity. [0] [1] Dependency injection (the pattern, not the framework) isn't viable for the same reasons as observers: you can't have a mutable reference field without causing a lot of headaches elsewhere. RAII isn't possible without sacrificing speed or safety. Most usages we see are backed by unsafe (FFI) or RefCell. We can't have multiple objects whose drop() affects the outside world, because we can't have multiple extant &mut references, and we can't just pass them in via parameters (because drop takes none). Back references aren't possible because of the circular problem (having a mutable reference to your owner means nobody else can read it). I'm not advocating for Hare, I'm just addressing the "I just don't see the excuse" remark, which can be ignorant of the costs of the borrow checker. The borrow checker is a great step forward, but not always a good tradeoff. An architect needs to be aware of these sacrifices before going all-in on a paradigm. [0] https://stackoverflow.com/questions/37572734/how-can-i-implement-the-observer-pattern-in-rust https://stackoverflow.com/questions/37572734/how-can-i-imple... [1] https://www.reddit.com/r/rust/comments/pwqju6/is_there_an_underlying_reason_that_idiomatic_rust/ https://www.reddit.com/r/rust/comments/pwqju6/is_there_an_un...
- staticassertion 4y agoRAII is built into Rust. DI is trivial and I use it all the time, idk what you're trying to say there. Graphs require Rc, backreferences are trivial with Rc::weak. idk what tot ell you
- Rusky 4y agoYou are fundamentally misunderstanding the relationship between Rust's unique and shared mutabilities and the usual mutability in, say, C. The Rust equivalent of C mutability is Cell. Conversely, the C equivalent of Rust's unique references is a `restrict` pointer. Cell is meant to be applied at the "leaves" of a type where individual assignments take place. When used this way, there is no additional copying relative to the straightforward C version. (Bringing up Vec and heap allocations here is also complete nonsense; Cell has nothing to do with Clone.) Once you wrap your head around that, all your shared mutability examples translate over to Rust trivially. All the "sacrifices" in speed you are imagining are relative to `restrict`/`&mut T`, not the usual baseline of a simple mutable object.
- glowcoil 4y agoYou can't use RAII in Rust? What on earth could this possibly mean? RAII is an extremely pervasive pattern in Rust and is fundamental to many of the safe APIs in the standard library.
- verdagon 4y agoTo clarify, I was talking about making RAII, not using RAII. And it surprised me too, when I learned that the borrow checker rejects it. To see it in action: Have a Database object, and try to have multiple Transaction objects that might commit something to it, in their drop(). It's unfortunately not possible, because they can't all have a &mut Database as struct fields. We can sacrifice speed (by using Cell's copying or Rc's counting) or safety (by using unsafe). Most RAII we see uses unsafe FFI under the hood, which is why it was so surprising to me.
- glowcoil 4y agoRAII is a general pattern for tying resource management to the lifetime of objects such that resource allocation is tied to value construction and resource deallocation is tied to value destruction. The smart pointers for allocation in the Rust standard library (Box, Rc, and Arc) are examples of the RAII pattern, since memory allocation happens at creation time (Box::new()) and memory deallocation happens when the Box goes out of scope (in drop()). Another example of RAII in the standard library is File: opening a file means creating a value of type File, and dropping that value means closing the file. Yet another example are the smart-pointer guards used for accessing RefCell and Mutex: RefCell::borrow() returns a Ref, and Mutex::lock() returns a MutexGuard; the underlying value can only be accessed while the guard exists, and access is relinquished when the guard is dropped. Given all this, it's absurd to say that Rust doesn't support RAII — RAII is fundamental to the design of many of Rust's safe APIs. The very specific API design that you've described is not possible in Rust, but it is strange to equate this with the entirety of RAII. In any case, there are many alternative APIs (some with no sacrifice in speed or safety!) that are perfectly possible in Rust.
- dureuill 4y agoi think the real mistake was to have "exclusive references" be called "mutable references" in the language. I've taken the habit of saying "mut" as "mutually exclusive" for references. Of course you can't have each Statement keep an exclusive reference to a db object. They're exclusive! You need shared references for your DB, implying you need interior mutability. This is how Statements are implemented in real-world rust database drivers such as rusqlite (any operation on a db is done through a shared reference). The fact that a very real package is doing it proves that the pattern you're talking about is, in fact, possible.