Currently, these are the types used for strings and arrays, in a table format:
| | Array | String |
|----------|--------|--------|
| Growable | Vec<T> | String |
| Slice | &[T] | &str |
This seems OK until you try to, for example, add in a rope array-like type, and a rope string type based on a rope array of bytes:
| | Array | String | Rope array | Rope string |
|----------|--------|--------|--------------|--------------|
| Growable | Vec<T> | String | Rope<T> | RopeStr |
| Slice | &[T] | &str | RopeSlice<T> | RopeStrSlice |
See the problem? I’ve had to create four new types just to represent a single data structure. Sure, I can implement the string types as wrappers around the rope types, but that also requires duplicating methods like push
, insert
&c… I also see no way of removing code duplication between the slices and the growable versions. To me this seems ridiculous, and makes implementing a usable rope type very tedious. I propose the following scheme instead:
| | Array | String | Rope array | Rope string |
|----------|-----------|-----------|---------------|---------------------|
| Growable | Grow<[T]> | Grow<Str> | Grow<Rope<T>> | Grow<Str<Rope<u8>>> |
| Slice | &[T] | &Str | &Rope<T> | &Str<Rope<u8>> |
Basically what this does is add a new pointer type, Grow<T>
, that represents a value that can change in size (like today’s Vec
, String
&c.). It also makes str
a library type, Str
, that takes a single type parameter (defaulting to [u8]
) representing the structure that internally stores the string. Such a structure would have to implement a trait that provides methods such as pushing characters, iterating over characters &c. but not necessarily things like iterating over bytes and so on. This could make things like Str<[char]>
(a UTF-32 string with constant-time char indexing) possible as well.
This scheme means that all I have to do to add in my rope type is create a new unsized type, Rope<T>
, and implement the StrContainer
(or whatever it’s called) trait for Rope<u8>
. Instantly I have a growable rope array type (Grow<Rope<T>>
), a rope slice type (&Rope<T>
), a growable rope string type (Grow<Str<Rope<u8>>>
) and a rope string slice type (&Str<Rope<u8>>
). (These names are pretty long, so I’d probably create aliases.)
One great thing about this change is that it requires no changes to the language (AFAICT) other than removing &str
. It also makes the proposed Deref<str>
/Deref<[T]>
impls for String
/Vec
make a lot more sense, as it actually is dereferencing a pointer.
So, any thoughts? Is there a simpler way of implementing a rope array/array slice/string/string slice, without having to rethink the very nature of Rust strings and growable collections? ; )