oddnerd / rust

Handwritten reference implementations in Rust.
0 stars 0 forks source link

split immutable and mutable dope vector #53

Closed oddnerd closed 3 months ago

oddnerd commented 3 months ago

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:

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.

oddnerd commented 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.

oddnerd commented 3 months ago

Perhaps this ought to be better documented in Rustdoc to clarify that it is akin to &mut [T] specifically, not just any slice.