Rust is awesome. It’s fast and safe and unicorns and rainbows. Right until you type unsafe
, which is… a less than positive experience. The problem, as I see it, is that unsafe
code has been neglected under various battle cries like “unsafe should be ugly so people don’t use it”, “let’s implement inheritance first” and “meh”. I’d like to offer another perspective:
###When the only way to verify code is to read it, only readable code can be safe.
This is not a new idea, but the core philosophy of OpenBSD, and their track record speaks for itself. Those of you who haven’t peeked into their code yet, do it now, it’s a thing of beauty.
I want Rust to be the language that makes them drool. We have the potential, Rust is already viable alternative to C in a lot of areas, but we also have code like this:
*(offset(dest as *const u8, i as int) as *mut u8) = *offset(src, i as int);
With that in mind, enjoy my wishlist:
-
Introduce visual distinction between various scalar casts that deliberately lose information in different ways. This probably just needs some traits (
.as_unsigned()
,.truncate()
etc.). Requireunsafe
for scalaras
and point people towards the traits in the error message. Casting to same-signed larger ints should be done implicitly. -
The basic C types must be provided by the compiler. They come from the OS, not libc: we want to implement kernels and libc itself and presumably talk to C code on both sides. All kernels define their interfaces in terms of C types. A pure Rust kernel can ignore them either way.
-
Even if the above is rejected, the C types MUST NOT, under ANY circumstances, remain just
typedef
s. -
Require
unsafe
for anything that mentions raw pointers. The compiler cannot reason about raw pointers, therefore passing them around is unsafe too. This is a completely safe example from the docs:
// wrong (the CString will be freed, invalidating `p`)
let p = foo.to_c_str().as_ptr();
- Get rid of the idea of a “const pointer”. This is counterintuitive, but in practice they only serve as visual clutter. Explicit casts for half the arguments of every single C call is not readable, and thus not safe. Right now, my code actually seems nicer if I just
transmute
all the things. A pointer is a pointer is a pointer. C’sconst
is a guideline at best, it has no impact on linkage or what the code will actually do, and the binding writers will be more concerned with the API they present to the rest of Rust. The main reason we might want*const T
is to distinguish between “source” and “destination” pointers, but we can easily fix that with any one of: named arguments, documentation, IDE support,#[const]
, wrapping
copy_memory(Dst(buf), Src(input), Size(rem));
or simply by keeping C invocations uncluttered enough that the reader can actually keep track.
- Allow
unsafe
code full access to private items/struct fields/whatever. Yes, it’s unsafe and unstable and rabid wolves might eat my kitten, but I already know that from typingunsafe
. This would make low-level code much cleaner, as well as eliminate at least half the wonderfulraw
functions like
#[inline]
#[stable]
pub unsafe fn set_len(&mut self, len: uint) {
self.len = len;
}
#[experimental]
pub unsafe fn from_raw_parts(length: uint, capacity: uint,
ptr: *mut T) -> Vec<T> {
Vec { len: length, cap: capacity, ptr: ptr }
}
At least when I do it by hand, I won’t be surprised by the lack of asserts. All problems I could think of are easily solved by introducing an unsafe use
construct and feature gating it.
Of course, this will also need all private stuff to be exported, but I expect those will come in handy once we start stabilizing the ABI and wrapping Rust libraries for other languages.