4 ms·
> The thing is that, in Haskell, even when you attach a function to run during destruction, the runtime doesn't guarantee that the function will be called promp
by dan00 8y ago
> The thing is that, in Haskell, even when you attach a function to run during destruction, the runtime doesn't guarantee that the function will be called promptly, or even at all.
There's also no guarantee for Rust/C++ destructors to be called. It's certainly less of an issue then depending on the GC to being called, but if you need absolute correctness, then you shouldn't rely on the destructors.
- quietbritishjim 8y agoIf a variable has block scope in C++ (i.e. it is a local variable in a function) then its destructor is guaranteed to be called when the block is finished, regardless of whether that is due to a `return` statement or an exception being thrown (or a `break` or `continue`). In what sense do you disagree? If you allocate an object on the heap with `new` then its destructor isn't called automatically unless you make it so through some other mechanism, but GP comment clearly want claiming that. There are some situations where objects with block scope do not have their destructor called e.g. `_exit()` called, segfault, power cable pulled out. But in that sense nothing is guaranteed.
- DanWaterworth 8y agoIf you are consuming an API that provides an object with a destructor, you are correct, you can determine when destructors will be called. The issue is when you produce an API that contains objects with destructors. Since you are handing these entities off to unknown code, you cannot ensure that they will be dropped. This was a problem in scoped threads in Rust.
- siscia 8y agoCan you please dig deeper, that I am not sure I follow. In which case in rust you cannot be sure that "the drop" will be called?
- richardwhiuk 8y agoA Rc cycle causing a leak. See the very excellent http://cglab.ca/~abeinges/blah/everyone-poops/ http://cglab.ca/~abeinges/blah/everyone-poops/
- dbaupp 8y agoIf there's a cycle of strong references with Rc or Arc (or shared_ptr in C++), those objects still never get dropped/have their destructors called.
- quietbritishjim 8y agoI think that falls into the category I mentioned in the third paragraph of my comment: a serious pre-existing bug with other consequences will potentially cause the guarantee to be violated. A similar effect would happen if you had a double free that sometimes caused a crash, which is a similar level of programming mistake to creating a cyclic reference. To me it sits outside of a reasonable definition of "guaranteed".
- dbaupp 8y agoNo, typically, a reference cycle is fine. It results in valid memory that never gets read again, which is unfortunate but not dangerous, whereas double-frees can result in memory corruption. http://huonw.github.io/blog/2016/04/memory-leaks-are-memory-safe/ http://huonw.github.io/blog/2016/04/memory-leaks-are-memory-...
- zaphar 8y agoRc's drop will be called. But whether the exposed object's drop will be called is dependent on the reference count. But Rc would not work if the drop was not guaranteed to be called.
- steveklabnik 8y agoIf you have a reference cycle, the two Rcs will keep each other alive, and their Drops will not be called.
- flurrything 8y ago> In what sense do you disagree? Not the parent, but it is trivial to write C++ and Rust examples in which destructors of variables with block scope are not called. The std library of both languages do even come with utilities to do this: C++ structs: struct Foo { Foo() { std::cout << "Foo()" << std::endl; } ~Foo() { std::cout << "~Foo()" << std::endl; } }; { std::aligned_storage<sizeof(Foo),alignof(Foo)> foo; new(&foo) Foo; /* destructor never called even though a Foo lives in block scope and its storage is free'd */ } C++ unions: union Foo { Foo() { std::cout << "Foo()" << std::endl; } ~Foo() { std::cout << "~Foo()" << std::endl; } }; { Foo foo(); /* destructor never called */ } Rust: struct Foo; impl Drop for Foo { fn drop(&mut self) { println!("drop!"); } } { let _ = std::mem::ManuallyDrop::<Foo>::new(Foo); /* destructor never called */ } etc. > There are some situations where objects with block scope do not have their destructor called e.g. `_exit()` called, segfault, power cable pulled out. But in that sense nothing is guaranteed. This is pretty much why it is impossible for a programming language to guarantee that destructors will be called. Might seem trivial, but even when you have automatic storage, any of the things you mention can happen, such that destructors won't be reached. In general, C++, Rust, etc. cannot guarantee that destructors will be called, because it is also trivial to make that impossible once you start using the heap (e.g. a `shared_ptr` cycle will never be freed).
- DanWaterworth 8y agoSad to see you being downvoted when you are correct, see [1] > forget is not marked as unsafe, because Rust's safety guarantees do not include a guarantee that destructors will always run. This was a problem in Rust when scoped threads relied on destructors being run. [1] https://doc.rust-lang.org/std/mem/fn.forget.html https://doc.rust-lang.org/std/mem/fn.forget.html
- ChrisSD 8y agoThis thread lists all the ways drop may not be called in Rust: https://users.rust-lang.org/t/drop-guarantees/20230/ https://users.rust-lang.org/t/drop-guarantees/20230/ I'm not sure it's possible to force any code to be run (e.g. a process can be terminated at any time) although a closure might offer slightly stronger guarantees in some situations.