你应该怎么在Rust中做指针算术?

Dou*_*oug 36 rust

我知道答案是"你不应该"......但是为了争论,你应该怎么做?

例如,如果你想编写一个替代方案,Vec<T>那就有所不同.

我看到你可以通过将* mut T值转换u64并添加到它们中来制作"编译和运行的东西" ,然后将它们转换回* mut T并读取指针处的值(参见下面的示例).它似乎有效,但它留下了一些悬而未决的问题:

  1. 将一个* mut T指针总是适合u64

  2. write()当数据是来自的任意(即非托管类型)数据块时,是否会发生不安全的指针触发指针别名问题libc:calloc

  3. 这只能起作用,因为我使用的是原始类型(f64).如果这是一个真正的数据对象,我将forget()首先得到该对象; 但你只需write()一个* mut T到目标,然后高高兴兴地read()出来以后再如果类型是复杂的,有孩子的记录?

  4. 这真的是这样做的正确方法吗?这看起来非常尴尬.我期待找到一些不安全ptrtoint()/ inttoptr()配对,但我找不到那样的东西.

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);
}
Run Code Online (Sandbox Code Playgroud)

产量

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
Run Code Online (Sandbox Code Playgroud)

A.B*_*.B. 52

指针有指针算法的offset方法.

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) });
}
Run Code Online (Sandbox Code Playgroud)

产量

2
1
3
Run Code Online (Sandbox Code Playgroud)

https://doc.rust-lang.org/nightly/book/first-edition/unsafe.html

  • 您可能应该使用`items.as_ptr()`来获取指针,尽管您的方法也是如此... (3认同)
  • 如果要添加 usize,还要考虑使用 `ptr.add(...)` 而不是 `ptr.offset(...)`(这样可以避免可能错误地将 usize 转换为 isize)。 (3认同)