5 ms·
Maybe it’s only my take, but from what I understand, the author wants to easily create closures with mutable references and call them from anywhere, asynchronou
by xondono 5y ago
Maybe it’s only my take, but from what I understand, the author wants to easily create closures with mutable references and call them from anywhere, asynchronously?
And the complaint is that Rust semantics makes this hard. Well yes, Rust makes it uncomfortable to shoot your own foot, that’s kind of the point.
- uncomputation 5y agoThat was my impression too. Rust “makes it hard” for a reason: it’s not safe. If you work with the type system to prove that it is, you can go about and do your thing but Rust is about guarantees. That’s one great thing about it. You can’t ask it to just not guarantee something.
- api 5y agoThe author wants Go, Java, C#, or another GC’d language.
- valenterry 5y agoWhy would the first example with the database be "shooting your own foot"? I'm not a Rust dev, but in other languages that code makes perfect sense to me.
- xondono 5y agoFor the same reason that keeping a shared excel file in a network disk is a bad idea. The closure can write to database, and after the call, there’s no way to know if it has already written, or even if it’s writing to it right now.
- valenterry 5y agoWell, without further ado that's true. But the callback could of course set a result type somewhere (so, behave similar to a future/promise) and then we could know if/when it is finished and if it was successfully, or if it's still running (or broke without an error). But that's not Rust specific, that's just plain language-independent logic no?
- xondono 5y ago> But the callback could of course set a result type somewhere [..] then we could know if/when it is finished That would be a Mutex<>, the author mentions it in the comments. This would solve the “writing at the same time issue”, but not the lifetimes, so you’d need another change. Instead of trying the mess of implementing special closures, you could just make your db static.
- uncomputation 5y agoWhat if you do: my_fn(|| { db.store(42); }); db.drop(); (Assuming you can just drop the database object, the semantics don’t really matter but the idea is the same.) The OP wants an easy way to do this but this is just not an easy thing to do. How can the closure guarantee that the database object will exist if my_fn decides to sleep for a couple seconds? Most languages do not have this problem because either: they use a garbage collector (Java and it’s cousins, Python, Go) or they are not safe (C, C++). Rust is in a particularly difficult (and ambitious and exciting!) intersection where this problem is tricky.
- valenterry 5y agoAh, I see. So if there was a way to mark the DB as "ephemeral" or so, then it would be fine, but since the compiler can't know if the reference isn't deleted/freed, it doesn't compile? If I got it right, then that makes total sense.
- kzrdude 5y agoRust has &mut references and & references. The `&mut Database` sort of implies that the Database handle doesn't do its own locking or other needed state tracking - it just relies on the exclusivity of `&mut` in Rust to be sure that nobody else is modifying the database handle at the same time. If the API used `&Database`, then it would have to be more flexible for simultaneous use of the handle. And then the closure would be (a bit) easier to call too, because it's not tracking that exclusive reference. If the API used a reference counted `Database` handle, then it would release even more restrictions of lifetime management, etc. You can implement all of these in Rust. In other languages, you usually end up with alternative two or three. If you try to simultaneously take alternative 1 and use it in a way that's not supported, that would be shooting yourself in the foot. Except Rust says compile error instead.
- microtonal 5y agoMaybe it’s only my take, but from what I understand, the author wants to easily create closures with mutable references and call them from anywhere, asynchronously? I think you are misunderstanding the author's point. I think that they'd probably agree that the (lack of) ergonomics of closures are a necessary result of the safeguards that Rust provides. They point is more that, given that closures have somewhat frustrating ergonomics, it was probably a bad choice to base Rust's primary concurrency system on closures. Disclaimer: this is not my opinion, just paraphrasing what I believe the author's point is. I found the post a bit strange, it seems to end rather abruptly. It doesn't seem to address the main question it seems to raise: what does this mean for async Rust and why is this bad?
- pornel 5y agoRust has stopped depending on closures for async when it moved from experimental futures library to built-in async/await. Rust now easily supports mutable and temporary references in async blocks and across await points. Rust still doesn't support use-after-free, so you can't reference a temporary object and use it in another thread that will outlive it :) In TFA the second do_work_and_then() example doesn't compile, because it has a UAF vulnerability that borrow checker has detected and prevented.
- ipodopt 5y agoYou can't do that using anything here? https://doc.rust-lang.org/std/mem/index.html https://doc.rust-lang.org/std/mem/index.html Genuine question, I don't mess around with memory typically.
- eximius 5y agoforget takes owned values. So you wouldn't have a handle to them to use after.
- pornel 5y agomem::forget() is not crashy like free(). It merely prevents destructors from being run (e.g. you could do the same by storing the value in a global variable). It's a safe method, subject to usual safety checks. You can use an `unsafe {}` block to call literally libc::free(), or dereference a random raw C pointer, or do something else crashy. However, when talking about Rust's safety rules it's usually assumed we're not talking about code bypassing these rules on purpose.