4 ms·
There is definitely some mental cost. By making lifetimes explicit there are additional barriers in the way of doing certain things that are safe but involve in
by sirclueless 8y ago
There is definitely some mental cost. By making lifetimes explicit there are additional barriers in the way of doing certain things that are safe but involve invariants that are too high-level for rust's semantics. For example, taking mutable references to multiple elements of a container simultaneously.
- steveklabnik 8y agoThis cuts both ways. Because I can offload checking from my brain to the compiler, I feel personally that the mental cost is actually reduced overall. YMMV.
- unrealhoang 8y ago.split_at_mut(), there you go. But I understand that there are cases when Rust rejects correct program, that is where we need unsafe to build safe abstraction. Come back to additional mental cost, it’s just the same debate between static & dynamic typed language. Either you write more, specify constraints, and have the usage checked or you write less, imply your constraints and must satisfy the constraints of usage by yourself (or with helper tools/tests). To me that’s just “when” I have to spend the mental resource, and the total cost is roughly the same. But then, knowing the separation helps to choose which tool for which type of project.
- sirclueless 8y agoI don't think this is just about being more or less verbose. It's about the awkward ways it contorts the ways you write programs. For example, in C++ you might write an asynchronous job scheduler with an API like the following: void Scheduler::schedule(const Job& job, Result* result); void Scheduler::wait(); The consumer of this API in C++ has an easy job. They can create an array of results and iteratively call schedule(jobs[i], &results[i]) for each job, and then call wait(). A similar API in Rust would be a giant pain in the neck to use, because there's no simple way to collect the results. You'd likely want to scrap this API entirely and have Scheduler expose some kind of queue or something. My point is that these extra constraints are not just extra typing locally to specify how you're using lifetimes. You'll likely have to rethink the way you write APIs and whole programs in Rust because of the ways it restricts mutability. Which is OK, it's a trade-off, all I'm saying is the safety doesn't come for free.
- GolDDranks 8y agoWhile there might be all kinds of troubles _implementing_ that API, I don't think it wouldn't be that hard to use. You would need to have a valid "not ready" state for the results, because Rust requires that they are in some state at the moment they are constructed. From the lifetime perspective, the data structure where the results live should outlive the schedule calls – but that isn't a problem if you construct the array first. There will be a lifetime requirement but that's it. The mutability thing isn't a problem. You can allocate a vector of results and jobs, and then have a mutable iterator over them, zip them together and then iterate over that. There's no problem passing mutable references to each of the elements.
- sirclueless 8y agoThe problem is that schedule() needs to mutate result asynchronously at some point in the future. It can't do this if result is a mutable reference, since it needs to return immediately so borrowing a reference doesn't work. It also can't take ownership of result, since it has no way to give it back (short of changing the API, for example by making wait() return a collection of results). The right way to implement the API above is for schedule() to take Arc<Mutex<Result>>, and explicitly share mutable ownership over each individual result between some unspecified asynchronous execution mechanism and the caller of schedule(). This makes the calling code significantly more complicated as it needs to incur the overhead of atomic reference counting and a mutex per result, and must try to lock each result before using it, even if it knows there are no other owners of the result due to wait() returning.
- GolDDranks 8y agoI don't see the problem with returning immediately after borrowing a reference, if the scheduler lives shorter than the results (if it doesn't, holding the reference would be unsound): https://play.rust-lang.org/?gist=226f92bc311dbac05c112b10ba434db6&version=stable&mode=debug https://play.rust-lang.org/?gist=226f92bc311dbac05c112b10ba4...
- 8y ago
- empath75 8y agoIf you’re sure they’re safe then just put them in an unsafe block and wrap that in a safe api.
- edflsafoiewq 8y agoIMO the cost is worth it for the documentation alone. What lifetime a callee expects me to uphold for the arguments I pass is very often a mystery on which the docs are absolutely silent in C/C++. In Rust, it's part of the type signature and enforced by the compiler, so I can always read it off at a glance. This is a huge win in reading a new API.