ps4 - rust traduccion
¿Cómo se debe hacer aritmética de punteros en Rust? (1)
Sé que la respuesta es "no deberías" ... pero por el bien de la discusión, ¿cómo deberías hacerlo?
Por ejemplo, si desea escribir una alternativa a Vec<T>
que funcionó de manera diferente.
Veo que puede hacer ''algo que compila y ejecuta'' al transmutar * mut T
valores de * mut T
en u64
y agregarlos, luego transmutarlos de nuevo en * mut T
y leer el valor en el puntero (vea el ejemplo a continuación). Parece funcionar, pero deja algunas preguntas abiertas:
¿Un puntero a
* mut T
siempre encajará enu64
?¿La
write()
en un puntero no seguro desencadena problemas de alias de puntero cuando los datos son un bloque de datos arbitrario (es decir, no de tipo administrado) delibc:calloc
?Esto solo funciona porque estoy usando un tipo primitivo (
f64
). Si se tratara de un objeto de datos reales, primero tendría queforget()
el objeto; pero, ¿puede simplementewrite()
un* mut T
en un objetivo y luegoread()
nuevo felizmente más tarde si el tipo es complejo y tiene registros secundarios?¿Es esta realmente la manera correcta de hacer esto? Parece extremadamente incómodo. Esperaba encontrar algún
ptrtoint()
inseguroptrtoint()
/inttoptr()
, pero no puedo encontrar nada de eso.
Ejemplo
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);
}
Salida
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
Los punteros tienen un método de offset
para la aritmética de punteros.
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) });
}
Salida
2
1
3
https://doc.rust-lang.org/nightly/book/first-edition/unsafe.html