4 ms·
> To release memory we simply release the blob itself, so it's like a batched deallocation, sort of. The consequence here is that all of our types must be: > 1
by asplake 2y ago
> To release memory we simply release the blob itself, so it's like a batched deallocation, sort of. The consequence here is that all of our types must be:
> 1. trivially copyable (: Copy if we speak in Rust)
> 2. trivially destructible (i.e. they can't have impl Drop)
2 (Drop) seems obvious enough, but why 1 (Copy)? There’s a bit of Rust knowledge implicit there that I’m unaware of, and I might not be alone in that. Could someone explain?
- mmastrac 2y agoTrivially copyable and destructible means that they are plain-old data, with no allocations, associated resources or any other side-effects.
- LegionMammal978 2y agoTrivial destructibility alone is generally sufficient for that in Rust. Recall that all values (outside a Pin) are trivially moveable, so pretty much the only reason you'd want trivial copying is if you actually need more than one valid copy at once. (One exception is using Copy to express a poor man's Freeze bound, which is quite limited, and dubious in any case.)
- mmastrac 2y agoA type that is not Copy may have associated memory allocations, however. You cannot simply forget a range of !Copy structs without the risk of memory leaks.
- LegionMammal978 2y agoIf those !Copy structs deallocate their memory when dropped properly, then they aren't trivially destructible. If they were trivially destructible, then they would leak their memory regardless of whether you dropped or forgot them. That's what it means for a type to be trivially destructible: dropping it is a no-op.
- mmastrac 2y agoI think we're talking about the same thing -- the original post in the thread used "no impl Drop" as "trivially destructable", not "no functional difference between drop and forgetting".
- LegionMammal978 2y agoWell, I interpret "trivially destructible (i.e. they can't have impl Drop)" as meaning "the type itself can't impl Drop, nor can any of its fields or subfields", which is what it means for a type to be "trivially destructible" in Rust. But what do you mean by "trivially destructible" being different from "no functional difference between drop and forgetting"? Unless I'm mistaken, they are the same thing: if none of a type's subfields impl Drop, then dropping it does absolutely nothing; whereas if any subfield does impl Drop, then it can't be a no-op, since the compiler has to form a &mut reference to pass it into Drop::drop() (a distinction very relevant for unsafe code). Could you give me an example of a !Copy type that is "trivially destructible", but can't be forgotten without leaking memory?
- LegionMammal978 2y agoI'm pretty sure being trivially destructible is sufficient for this use case. The only reason you might want a Copy bound when you're not actually copying values is if you're trying to disallow interior mutability, but that doesn't seem to be the case here. Perhaps the author was misinformed. Or maybe they're just using Copy as a type-level bound that guarantees a trivial destructor, even if it's more restrictive than strictly necessary.
- MindSpunk 2y agoYou can't have a negative trait bound to say "I take anything that _doesn't_ implement Drop". It's not a stabilized feature in Rust because it would allow adding a trait to a struct to become a breaking API change where it currently is not. Copy guarantees !Drop so it's currently the only way to do it without opting into nightly features. You can remove the requirement for trivially destructible types by maintaining a singly linked list of (ptr to alloc'd object, destructorFn) pairs with the links allocated from the arena itself. The arena can just walk the list and call all the drop functions when it gets reset. You can even specialize for types with trivial destructors so they don't append to the list and so you only pay the extra cost if you allocate Drop types (using std::mem::needs_drop).
- LegionMammal978 2y ago> You can't have a negative trait bound to say "I take anything that _doesn't_ implement Drop". It's not a stabilized feature in Rust because it would allow adding a trait to a struct to become a breaking API change where it currently is not. Copy guarantees !Drop so it's currently the only way to do it without opting into nightly features. Alternatively, you could just do an std::mem::needs_drop() check when inserting an element, and fail if it indicates that it would need to be dropped. (Yeah, its documentation protests that it can have false positives, but IIRC you have to be doing super wacky type-level stuff to trigger that for real.) That's why I said that Copy is sufficient (but restrictive) as a type-level bound, since needs_drop() can't be done on that level. At least, not without the trick of throwing a post-mono error with an associated const.
- GrantMoyer 2y agoI think it's a mistake. Allocator Implementation[1] shows values being moved into the arena, which can be done without Copy or even Clone. Maybe the author got confused, since moving an object in Rust just copies the bytes and invalidates the moved from binding. [1]: https://iliabylich.github.io/arena-based-parsers/allocator_implementation.html https://iliabylich.github.io/arena-based-parsers/allocator_i...