Description
Right now a few places in the code base make assumptions about the internal representations of things like &[T]
or other types that stand for internal representation details, mostly to implement low-level operations in unsafe blocks. For example you could see code like this:
pub fn my_slice<'r,T>(v: &'r [T], start: uint, end: uint) -> &'r [T] {
assert!(start <= end);
assert!(end <= v.len());
unsafe {
let mut (buf, len): (*T, uint) = transmute(v);
buf += start;
len = end - start;
transmute(buf, len)
}
}
This is fine as long as the internal representation of those things doesn't change, but will wreak random havoc if something changes (for example as result of the implementation of the Dynamic types proposal, or a change to the way &str
handles its hidden trailing byte)
To improve on this situation, I think the following needs to be done for all build-in 'special' types like &[T]
, &str
, char
etc:
- Have a central place in
std::unstable
for the underlying representation. It would contain things likestruct SliceRep<T> { buf: *T, len:uint }
orstruct CharRep { v: uint }
. - Have functions to safely cast a type to its repr, to unsafely cast a repr to its type, and if possible functions to safely cast a repr to it's type provided all necessary invariants are unviolated.
- Implement a lint warning for calls of
transmute
-like functions from/to those types. All low level work and casts to representations would then have to happen through the functions in the corresponding modules, where the warnings get explicitly disabled with an attribute.
The canonical warn-free way to write my_slice
would then become something like this:
pub fn my_slice<'r,T>(v: &'r [T], start: uint, end: uint) -> &'r [T] {
assert!(start <= end);
assert!(end <= v.len());
let mut repr = v.to_internal_repr();
unsafe {
repr.buf += start;
repr.len = end - start;
repr.to_type()
}
}