Pointers and Unsafe
Safe code cannot read arbitrary memory. Raw pointers exist, but every
dereference, arithmetic, or cast on them must happen inside an unsafe block.
Address-of is safe
Taking the address of a local is safe. Dereferencing requires unsafe.
use "std/io.cnd";
fn main() -> i32 {
let mut x = 5;
let p = &x;
unsafe {
*p += 10;
}
print(&x, .I32);
putchar(10);
return 0;
}
$ ./pointers
15
Passing pointers to functions
The usual "method" pattern: pass &value, mutate through the pointer.
use "std/io.cnd";
fn swap(a: *i32, b: *i32) {
unsafe {
let t = *a;
*a = *b;
*b = t;
}
}
fn main() -> i32 {
let mut x = 1;
let mut y = 2;
swap(&x, &y);
print(&x, .I32);
let sp: []u8 = " ";
print(&sp, .S);
print(&y, .I32);
putchar(10);
return 0;
}
$ ./swap
2 1
Pointer to a slice byte
A string is a slice; s.ptr points at its first byte.
use "std/io.cnd";
fn main() -> i32 {
let s = "hello";
let p = s.ptr;
unsafe {
let first = *p;
let v: i32 = first as i32;
print(&v, .I32);
}
putchar(10);
return 0;
}
$ ./strptr
104
null
The literal null is the null pointer. Never dereference it.
use "std/io.cnd";
fn main() -> i32 {
let p: *i32 = null;
if p == null {
println("null pointer");
}
return 0;
}
$ ./null
null pointer
Pointer qualifiers
const reads only; volatile reads/writes go around optimization.
let a: *const u32 = &value; // read-only
let b: *volatile u32 = ® // volatile access
let c: *const volatile u32 = ®
*void
void is a pointer to an unknown type. Any pointer coerces to void;
converting back requires an unsafe cast.
use "std/io.cnd";
fn main() -> i32 {
let mut n: u64 = 42;
let opaque: *void = &n;
unsafe {
let back: *u64 = opaque as *u64;
let v = *back;
print(&v, .U64);
}
putchar(10);
return 0;
}
$ ./voidptr
42
Pointer arithmetic
Arithmetic on pointers (and indexing) is unsafe.
use "std/io.cnd";
fn main() -> i32 {
let arr = [10, 20, 30];
let base = arr[..].ptr; // convert the array to a slice, take its ptr
unsafe {
let v1 = *base;
print(&v1, .I32);
let sp: []u8 = " ";
print(&sp, .S);
let v2 = *(base + 1);
print(&v2, .I32);
putchar(10);
}
return 0;
}
$ ./ptradd
10 20
unsafe functions
A function marked unsafe may be called only inside an unsafe block. Its
body is already unsafe.
use "std/io.cnd";
unsafe fn read_u32(p: *u32) -> u32 {
return *p;
}
fn main() -> i32 {
let v: u32 = 7;
unsafe {
let got = read_u32(&v);
let out: i32 = got as i32;
print(&out, .I32);
putchar(10);
}
return 0;
}
$ ./unsafe_fn
7
asm
Inline assembly is available inside unsafe.
// docs: kernel
unsafe {
asm("wfi");
}
See Bare Metal for how this is used in a kernel.