Подтвердить что ты не робот

Как вы должны делать арифметику указателей в Rust?

Я знаю, что ответ "вы не должны"... но ради аргумента, как вы это делаете?

Например, если вы хотите написать альтернативу Vec<T>, которые работают по-другому.

Я вижу, что вы можете сделать "что-то, что компилируется и запускается", преобразовывая значения * mut T в u64 и добавляя к ним, а затем передавая их обратно в * mut T и считывая значение в указателе (см. пример ниже). Кажется, он работает, но он оставляет несколько открытых вопросов:

  • Будет ли указатель * mut T всегда вписываться в u64?

  • Выполняет ли write() обращение к небезопасным вопросам смены указателя триггера указателя, когда данные представляют собой произвольный (то есть не управляемый тип) блок данных из libc:calloc?

  • Это работает только потому, что я использую примитивный тип (f64). Если бы это был реальный объект данных, мне пришлось бы forget() объект сначала; но можете ли вы просто write() a * mut T в цель, а затем счастливо read() повторить это позже, если тип является сложным и имеет дочерние записи?

  • Это действительно правильный способ сделать это? Это кажется крайне неудобным. Я ожидал найти небезопасную пару 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);
}

Выход

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
4b9b3361