Hi, folks.
I have an idea.
Rust has special traits (kinds), supported by the compiler: Send
, Sized
, Copy
etc.
I think that one important type kind is missing: RawData
(better name suggestions are welcome).
RawData
means that it is safe to read uninitialized data of that type (in sense that it won’t crash the process) and it is safe to write random bits to it.
RawData
types are:
- all ints
- tuples, structs and fixed size arrays of
RawData
Pointers and enums are not RawData
even if they are Copy
.
Note, variables of type RawData
still have to be initialized like any other variable.
RawData
has two major applications.
1) To avoid initialization of a buffer before reading into it.
Currently to read data, user has to initialize it with zeros, which is unnecessary work:
let mut buffer: Vec<u8> = Vec::new();
// Unnecessary memset, because data
// is overwritten in the next line
buffer.grow(1 << 20, 0);
my_reader.read_into(buffer.as_mut_slice());
This problem can be solved by using unsafe set_len
function, which is inconvenient, because code becomes, well, unsafe.
If Rust had RawData
trait, Vec<T>
could have a function:
impl <T : RawData> Vec<T> {
fn grow_no_init(&mut self, len: uint) { ... }
...
}
That fn grow_no_init(..)
solves performance issue without hurting application safety (in sense that application won’t crash, see below).
For example, with that or similar function, Reader::push_at_least(..)
can be implemented without unsafe code.
2) For parsers
Sometimes is it convenient to have serialized data be mapped to structure. For example to parse IPv6 header one could use a struct:
#[packed]
#[repr(C)]
struct Ipv6Header {
ver_cls_label: u32,
payload_length: u16,
next_header: u8,
hop_limit: u8,
src_address: Ipv6Address,
dest_address: Ipv6Address,
}
&[T]
where T
is RawData
may have
// return None if alignment does not match
fn bitcast<U : RawData>(&self) -> Option<U> { ... }
So it can be use it like this:
let packet_data: &[u8] = ...
let header_slice = packet_data
.bitcast<Ipv6Header>().unwrap();
if header_slice.len() < 1 {
// buffer does not contain enough data
return;
}
let header = header_slice[0];
...
Safety concerns
Because grow_no_init()
can cause leakage of sensitive information stored previously in the heap and freed, grow_no_init()
function still should probably be unsafe
. However, this function is still safer than set_len()
.
Anyway, simply presence of RawData
type kind does not allow users to read uninitialized data. RawData
just tells some properties of a type. It is up to library authors to decide whether their functions like grow_no_init
should be safe or unsafe.