ps4 - rust traduccion
¿Acceso conveniente a los miembros de punteros crudos? (3)
Puede implementar un método personalizado para punteros sin procesar exactamente de la misma manera que para cualquier otro tipo de Rust:
trait WickedRef<T>{
unsafe fn wicked_ref<''x>(self) -> &''x T;
}
impl<T> WickedRef<T> for *mut T{
unsafe fn wicked_ref<''x>(self) -> &''x T{
&*self
}
}
root.link.wicked_ref().link.wicked_ref()
La notación para acceder a los miembros anidados de los punteros sin procesar para las instancias que sabemos que no necesitan verificarse contra NULL puede ser bastante incómoda:
struct MyLink {
link: *mut MyLink,
}
let var = *(*(*(*root).link).link).link;
¿Se puede acceder a los miembros de estructura del puntero sin tener que desviar cada vez explícitamente? ¿Quizás usando métodos como root.link().link().link()
o envolviendo el tipo?
Mientras Rust idiomática lo evita, hay casos excepcionales en los que no es tan fácil de evitar. Rc
tiene una sobrecarga de memoria, el comprobador de préstamos causa problemas para miembros que se entrelazan, C-API puede requerir punteros ... etc.
Los métodos Wrapper pueden mejorar la legibilidad de ese código. Solo siguiendo El Libro :
struct MyLink {
link: *mut MyLink,
pub n: i32,
}
impl MyLink {
pub unsafe fn link(&self) -> &MyLink {
&*self.link
}
pub unsafe fn mut_link(&mut self) -> &mut MyLink {
&mut *self.link
}
}
Si marcar el prototipo del método como unsafe
o no depende de su caso particular, pero la implementación debe realizarse en un bloque inseguro: recuperar una referencia desde un puntero, incluso sin desreferenciar, no es seguro.
Utilizándolo:
unsafe {
let mut l1 = MyLink {
link: 0 as *mut MyLink,
n: 4,
};
let mut l2 = MyLink {
link: &mut l1 as *mut MyLink,
n: 3,
};
let n1 = l2.n;
let n2 = l2.link().n;
println!("{} -> {}", n1, n2);
}
Si esta es una situación recurrente en su código, solo crearía un contenedor genérico.
#[repr(C)]
#[derive(Hash)]
struct Ptr<T> {
ptr: *mut T
}
impl<T> Ptr<T> {
pub unsafe fn new(ptr: *mut T) -> Ptr<T> {
debug_assert!(!ptr.is_null());
Ptr { ptr: ptr }
}
#[inline(always)]
pub fn as_pointer(&self) -> *mut T {
self.ptr
}
}
impl<T> Deref for Ptr<T> {
type Target = T;
#[inline(always)]
fn deref(&self) -> &T {
unsafe { &*self.ptr }
}
}
impl<T> DerefMut for Ptr<T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
unsafe { &mut *self.ptr }
}
}
impl<T> Copy for Ptr<T> { }
impl<T> Clone for Ptr<T> {
#[inline(always)]
fn clone(&self) -> Ptr<T> { *self }
}
impl<T> PartialEq for Ptr<T> {
fn eq(&self, other: &Ptr<T>) -> bool {
self.ptr == other.ptr
}
}
Afirmamos al momento de la construcción que el ptr
es efectivamente nulo, por lo que no es necesario volver a verificarlo cuando elimine la referencia.
Luego dejamos que el lenguaje verifique Deref
/ DerefMut
cuando se llama a un método o se accede a un atributo:
struct MyLink {
link: Ptr<MyLink>,
}
fn main() {
let mut link = MyLink { link: unsafe { Ptr::new(1 as *mut _) } };
let next = MyLink { link: unsafe { Ptr::new(&mut link as *mut _) } };
let _ = next.link;
}