How should you do pointer arithmetic in Rust?
rust
Solution
Pointers have an `offset` method for pointer arithmetic.
fn main() {
let items = [1usize, 2, 3, 4];
let ptr = &items[1] as *const usize;
println!("{}", unsafe { *ptr });
println!("{}", unsafe { *ptr.offset(-1) });
println!("{}", unsafe { *ptr.offset(1) });
}
Output
2
1
3
https://doc.rust-lang.org/nightly/book/first-edition/unsafe.html
Problem
I know the answer is "you shouldn't"... but for the sake of argument, how should you do it? For example, if you wanted to write an alternative to `Vec<T>` that worked differently. I see that you can make 'something that compiles and runs' by transmuting `* mut T` values into `u64` and adding to them, then transmuting them back into `* mut T` and reading the value at the pointer (see example below). It seems to work, but it leaves a few of open questions: Will a `* mut T` pointer always fit into `u64`? Does `write()`ing to an unsafe pointer trigger pointer aliasing issues when the data is an arbitrary (i.e. not a managed type) data block from `libc:calloc`? This only works because I'm using a primitive type (`f64`). If this was a real data object, I would have to `forget()` the object first; but can you simply `write()` a `* mut T` into a target and then happily `read()` it out again later if the type is complex and has child records? Is this really the right way of doing this? It seems extremely awkward. I was expecting to find some unsafe `ptrtoint()` / `inttoptr()` pair, but I can't find anything like that. Example ``` extern crate libc; use std::mem::size_of; use std::ptr::write; use std::ptr::read; use std::mem::transmute; use libc::calloc; use libc::free; use libc::c_void; struct Array { length: usize, data: *mut f64, } impl Array { fn new(length: usize) -> Array { unsafe { Array { length: length, data: calloc(size_of::<f64>(), length) as *mut f64, } } } fn set(&mut self, offset: usize, value: f64) { if offset < self.length { unsafe { let root: *mut f64 = transmute(transmute::<*mut f64, u64>(self.data) + (size_of::<f64>() * offset) as u64); println!("Write: [{:?}] -> {}", root, value); write(root, value); } } else { println!("Write: Nope: [{}] is out of bounds", offset); } } fn get(&self, offset: usize) -> f64 { if offset < self.length { unsafe { let root: *const f64 = transmute(transmute::<*mut f64, u64>(self.data) + (size_of::<f64>() * offset) as u64); let rtn = read::<f64>(root); println!("Read: [{:?}] -> {}", root, rtn); return rtn; } } println!("Read: Nope: [{}] is out of bounds", offset); 0.0 } } impl Drop for Array { fn drop(&mut self) { unsafe { free(self.data as *mut c_void); } } } fn main() { let mut tmp = Array::new(4); tmp.set(0, 100.5); tmp.set(1, 101.5); tmp.set(2, 102.5); tmp.set(3, 103.5); tmp.set(4, 104.5); tmp.get(0); tmp.get(1); tmp.get(2); tmp.get(3); tmp.get(4); } ``` Output ``` Write: [0x7f04bdc1e080] -> 100.5 Write: [0x7f04bdc1e088] -> 101.5 Write: [0x7f04bdc1e090] -> 102.5 Write: [0x7f04bdc1e098] -> 103.5 Write: Nope: [4] is out of bounds Read: [0x7f04bdc1e080] -> 100.5 Read: [0x7f04bdc1e088] -> 101.5 Read: [0x7f04bdc1e090] -> 102.5 Read: [0x7f04bdc1e098] -> 103.5 Read: Nope: [4] is out of bounds ```