Closed oddnerd closed 3 months ago
The current trait hierarchy assumes an owning object which generally makes sense since owners of a data structure presumably own the elements. The dope vector is the first data structure so far to be inherently referential therefore being incapable of owning the underlying elements. Splitting the interface across a mutability boundary would require likewise bifurcating the entire existing trait hierarchy which is undesirable because it would compound typical owning structures with meaningless reference semantics. An Array
or the like should ideally have its mutability decided by the mutability of the instance rather than what trait interface is used to interact with it. Looking into the implementation details of slices ([T]
) within Rust, I think it is significant that from_raw_parts
and friends yield references to a slice rather than an actual instance of the slice type effectively allowing the constructor to produce an immutable instance. This is possible only because it is a primitive (built-in) type.
Since the pointer constructor already requires a mutable pointer (NonNull
) whereas the slice cast requires a mutable slice (&mut [T]
), in retrospect I think the safety comment is sufficient since it defines the structure to be inherently mutable (owning mutable references) which is consistent with the method signatures. By passing responsibility to the caller, they themselves must cast an immutable pointer to mutable which makes the potential bug visible and prevents naively constructing from immutable memory.
Perhaps this ought to be better documented in Rustdoc to clarify that it is akin to &mut [T]
specifically, not just any slice.
The current dope vector implementation merely has a safety comment encouraging the caller to not use mutable methods on an instance created from immutable memory. Instead, interfaces should exist which separate functionality so creating from a constant pointer results in an immutable instance, whereas a mutable instance must be obtained from a mutable pointer. It would still be possible for users to manually cast the mutability of pointers as is inherent to the language, but this change would make it an obvious bug like it ought to be instead of expected convention which hides potential bugs.
Two public traits should be created with associated methods:
Dope
DopeMut
with then a single private struct implementing both traits. This means there would still be one underlying implementation object, yet the immutable and mutable interfaces would be separate.