Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should you do pointer arithmetic in Rust?

Tags:

rust

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:

  1. Will a * mut T pointer always fit into u64?

  2. 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?

  3. 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?

  4. 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 
like image 204
Doug Avatar asked Jul 15 '14 13:07

Doug


People also ask

Should I use pointers in Rust?

Much of Rust's safety comes from compile-time checks, but raw pointers don't have such guarantees, and are unsafe to use. *const T and *mut T are called 'raw pointers' in Rust.

How do pointers work in Rust?

A pointer is a general concept for a variable that contains an address in memory. This address refers to, or “points at,” some other data. The most common kind of pointer in Rust is a reference, which you learned about in Chapter 4. References are indicated by the & symbol and borrow the value they point to.

What does pointer arithmetic do?

Address arithmetic is a method of calculating the address of an object with the help of arithmetic operations on pointers and use of pointers in comparison operations. Address arithmetic is also called pointer arithmetic.


1 Answers

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

like image 159
A.B. Avatar answered Oct 12 '22 03:10

A.B.