4 ms·
> destructor leaks Right, I'm not saying C++ is perfect either. I feel like Rust made a wrong turn here: https://www.reddit.com/r/rust/comments/3404ml/prepoopi
by cousin_it 11y ago
> destructor leaks
Right, I'm not saying C++ is perfect either. I feel like Rust made a wrong turn here: https://www.reddit.com/r/rust/comments/3404ml/prepooping_your_pants_with_rust/ https://www.reddit.com/r/rust/comments/3404ml/prepooping_you...
> reliance on macros
I'm referring specifically to try!.
- kibwen 11y ago> I feel like Rust made a wrong turn here I think the broader context of "the leakpocalypse" involved a host of difficult decisions, all of them suboptimal in their own ways. Of all the options on the table, it's my opinion that Rust took the most pragmatic one, especially given that the proposals that sought to ensure "never-leak" behavior all felt like they had undiscovered holes of their own, leading to a lot of ceremony for no actual benefit. > I'm referring specifically to try! I'm also referring to `try!`. :) It's a personal goal of mine this year to advocate a language-level replacement (and enhancement) for improved error-handling ergonomics.
- Manishearth 11y agoNote that whilst the leakpocalypse made it safe to leak, it's not a common case. Leaking safely involves an explicit call to mem::forget or extremely convoluted code involving Rc cycles. Its pretty much going to happen in languages which provide refcounted pointers. None of the proposed solutions mitigated this either. You will always be able to leak cycles. The difference was that some proposals made it possible to mark types as non-leakable, in which case they can't be placed in things which may potentially leak. The fact that things will leak was never under question there. The decision to be made was on how safety should interact with it -- should there be a way for a type to rely on its destructor being run for the purposes of safety? The decision was that there shouldn't, and we ended up fixing two APIs which relied on destructors being run for safety, instead of adding a major language change.
- cousin_it 11y agoFor what it's worth, I feel that marking types is also a mistake, and the whole machinery of OIBITs exists due to some unfortunate decisions made early on. Maybe the cleanest solution to the leakpocalypse would be to mark RefCell.borrow_mut as unsafe, because it's problematic in two separate ways (it can both panic and create cycles). That way the safe fragment of Rust would guarantee that memory leaks can't happen and destructors always run. That would fit with the spirit of Rust, which strongly prefers 100% guarantees to 99% guarantees. Though I guess that idea was judged too inconvenient for users, and I don't claim to know what users want.
- dbaupp 11y agoConcurrency/parallelism is sometimes described as "abstraction piercing", i.e. you can't tell if something is safe for use in a concurrent code by just looking at its interface, the internals of the type and the implementation details of its functionality matter. One way to handle this is to have each type explicitly opt-in to saying they're concurrent-safe, but this is fairly annoying, as it requires a lot of boilerplate. Boilerplate that is mostly pointless in Rust, because its rules mean most types people define will be safe, and most types that aren't safe will be built out of existing types that aren't safe. OIBITs are proxies for "safe", and are designed to strict a balance, by basically following that rule (types made out of types implementing an OIBIT automatically impl that OIBIT too). Moving on to your specific suggestion: RefCell isn't the only way to not run destructors, for instance, one can create ownership cycles with Mutex and RWLock in a similar way, and, more broadly, threads that dead lock (or live lock) will leak their resources, as would calling std::process::exit. There's also weirder/wilder things like the following: suppose Queue<T> is a multiple-producer multiple-consumer queue type, where any instance can act as both a producer and a consumer (typical for such types), then the following code creates an ownership cycle within the queue, and so won't run the destructor of X: struct Msg { x: X, y: Queue<Msg> } let q: Queue<Msg> = ...; let q2 = q.clone(); // another handle to the same queue q.send(Msg { x: ..., y: q2 }); drop(q); Now, sure you could mark all these things unsafe, but I think that starts to make things very inconvenient, and, somewhat, weaken what `unsafe` means (it normalise its use since those things are typically totally fine). (Also, those examples are almost surely not exhaustive.)
- pcwalton 11y agoActually, I'm increasingly convinced we made the right choice there. In some other areas (exception safety) we're discovering that adding new traits to the language that infect everything has dramatically negative effects on usability. I think of things like the leakpocalypse the same way I think of security advisories like the Go one from a few days ago. Yeah, it looks bad on paper, but the odds of being affected by it in practice are so incredibly low that it's not worth worrying about.
- Manishearth 11y agoAs far as try goes, see https://github.com/rust-lang/rfcs/pull/243#issuecomment-172057844 https://github.com/rust-lang/rfcs/pull/243#issuecomment-1720... . It's reached final comment period so probably will happen soonish?