You'd have to manually implement the traits to support the ergonomics of slices and iteration and costly reallocation if you need to pass ownership as a Vec:
I'd expect:
pub struct NonEmpty<T> {
v: Vec<T>,
}
The constructor would enforce the invariant and then you'd impl Deref and DerefMut for [T] to gain normal len/is_empty/indexing/iteration, passing as &[T] to other funcs and mutating values (which can't break the invariant).
To mutate length while preserving the invariant it's dealers choice e.g.
- add .into_vec() for unwrap/mutate/rewrap
- add invariant preserving mutators of your choice
There's a whole follow up post about this: <a href="https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-type-safety/" rel="nofollow">https://lexi-lambda.github.io/blog/2020/11/01/names-are-not-...
Not sure switching between languages makes for a compelling argument:
"Look how easy it is to accidentally bypass the invariant of a rust newtype by transliterating the data shape into Haskell and deriving a new type". Uh, ok.
If comparing the "risk of accident" between a newtype wrapper whose only role is enforcing the the invariant versus manually reimplementing vector and iterator semantics to use a different layout... I'd say the newtype wins that.
It would be good advice to keep a newtype that enforces an invariant as a single purpose primitive type. A building block and not a place to add other features.
There might be times I'd prefer structural enforcement e.g. something serialisation related. Converting into a non-rust format is what they are doing in their "accident"!
Fluorescence · · focus · HN ↗
I'd expect:
The constructor would enforce the invariant and then you'd impl Deref and DerefMut for [T] to gain normal len/is_empty/indexing/iteration, passing as &[T] to other funcs and mutating values (which can't break the invariant).To mutate length while preserving the invariant it's dealers choice e.g.
- add .into_vec() for unwrap/mutate/rewrap
- add invariant preserving mutators of your choice
Rusky · · focus · HN ↗
Fluorescence · · focus · HN ↗
"Look how easy it is to accidentally bypass the invariant of a rust newtype by transliterating the data shape into Haskell and deriving a new type". Uh, ok.
If comparing the "risk of accident" between a newtype wrapper whose only role is enforcing the the invariant versus manually reimplementing vector and iterator semantics to use a different layout... I'd say the newtype wins that.
It would be good advice to keep a newtype that enforces an invariant as a single purpose primitive type. A building block and not a place to add other features.
There might be times I'd prefer structural enforcement e.g. something serialisation related. Converting into a non-rust format is what they are doing in their "accident"!
[deleted] · · focus · HN ↗
[deleted]