简体   繁体   English

是否可以在 Rust 中使用指针访问结构的元素?

[英]Is it possible to access elements of a struct using pointers in Rust?

In C, we can access individual elements of a struct via pointers.在 C 中,我们可以通过指针访问结构的各个元素。 How do we do the same in Rust?我们如何在 Rust 中做同样的事情?

The code below shows how to access elements using pointers in C.下面的代码显示了如何在 C 中使用指针访问元素。

#include <stdio.h>
#include <stdlib.h>

typedef struct __attribute__ ((packed)) {
    int a;
    int b;
    int c;
} Data;

Data* new_data(const int a, const int b, const int c) {
     Data* data = malloc(sizeof(Data));
     data->a = a;
     data->b = b;
     data->c = c;
     return data;
}

int main(int argc, char* argv[]) {
    Data* data = new_data(23, 35, 12);

    // accessing elements via pointers
    printf("\n --- Using pointers ---\n");
    printf("a: %d\n", *((int*)data + 0));
    printf("b: %d\n", *((int*)data + 1));
    printf("c: %d\n", *((int*)data + 2));

    // using pointer magic
    printf("\n --- Using pointer magic ---\n");
    printf("b: %d\n", *((int*)((char*)data + sizeof(int))));
    printf("c: %d\n", *((int*)((char*)data + sizeof(int) * 2)));

    // accessing elements via name
    printf("\n --- Using names ---\n");
    printf("a: %d\n", data->a);
    printf("b: %d\n", data->b);
    printf("c: %d\n", data->c);

    free(data);
    return 0;
}

The above is compiled using gcc and I'm aware it's also platform specific, but it's not my concern.以上是使用gcc ,我知道它也是特定于平台的,但这不是我关心的。

The below is what I currently have in Rust.以下是我目前在 Rust 中的内容。

struct Data<T> {
    el: Vec<T>
}

impl <T> Data<T> where T: Copy {
    fn new(a: T, b: T, c: T) -> Self {
        let mut s = Self { el: Vec::with_capacity(3) };
        s.el.push(a);
        s.el.push(b);
        s.el.push(c);
        return s;
    }

    fn get_a(&self) -> T { self.el[0] }
    fn get_b(&self) -> T { self.el[1] }
    fn get_c(&self) -> T { self.el[2] }
}

fn main() {
    let mut data = Data::new(23, 35, 12);
    println!("data capacity: {:?}", data.el.capacity());

    println!("a: {:?}", data.get_a());
    println!("b: {:?}", data.get_b());
    println!("c: {:?}", data.get_c());
}

I'd like to be able to use我希望能够使用

struct Data<T> {
    a: T,
    b: T,
    c: T
}

and access each element via their index.并通过它们的索引访问每个元素。

In the general case, there is no way to do this correctly in Rust today.在一般情况下,今天在 Rust 中没有办法正确地做到这一点。 However, your particular struct avoids some of the worst problems, making it safe to borrow the entire struct as a borrowed slice ( &[T] ).但是,您的特定结构避免了一些最严重的问题,从而可以安全地将整个结构作为借用切片 ( &[T] ) 借用。 To do so you need to do three things:为此,您需要做三件事:

  1. Mark the struct repr(C) , but not repr(packed) !标记 struct repr(C)但不标记repr(packed) Packed structs are unaligned, and references must always be properly aligned.压缩结构是未对齐的,并且引用必须始终正确对齐。

  2. Check that the size of the struct is no larger than isize::MAX .检查结构的大小是否不大于isize::MAX

  3. Useslice::from_raw_parts to borrow a &[T] from the &Data<T> .使用slice::from_raw_parts&Data<T>借用&[T]

For a point-by-point justification of why this is sound, see Is it legal to cast a struct to an array?有关为什么这是合理的逐点证明,请参阅将结构强制转换为数组是否合法?

#[repr(C)]
struct Data<T> {
    pub a: T,
    pub b: T,
    pub c: T,
}

impl<T> Data<T>
where
    T: Copy,
{
    fn new(a: T, b: T, c: T) -> Self {
        Data { a, b, c }
    }

    // N.B. you could also implement `AsRef<[T]>` and/or `Borrow<[T]>`, which
    // are used frequently in generic code
    fn as_slice(&self) -> &[T] {
        assert!(std::mem::size_of::<Self>() <= isize::MAX as _);
        // This `unsafe` block was copied from Stack Overflow without proving
        // its use is correct in this context, so it's almost certainly wrong
        unsafe { std::slice::from_raw_parts(self as *const _ as *const T, 3) }
    }
}

Here's a test function to add confidence that casting to a slice works.这是一个测试函数,用于增加对切片进行转换的信心。

unsafe fn test_slice_data_equiv<T: Clone>(t: &T) {
    let data = Data { a: t.clone(), b: t.clone(), c: t.clone() };
    let slice: [T; 3] = [ t.clone(), t.clone(), t.clone()];
    fn as_u8_ptr<U>(r: &U) -> * const u8 {
        r as * const U as * const u8
    }
    let data_ptr = as_u8_ptr(&data);
    let slice_ptr = as_u8_ptr(&slice);
    
    assert_eq!(as_u8_ptr(&data.a).offset_from(data_ptr),
               as_u8_ptr(&slice[0]).offset_from(slice_ptr),
               "Data.a != slice[0]");
    assert_eq!(as_u8_ptr(&data.b).offset_from(data_ptr),
               as_u8_ptr(&slice[1]).offset_from(slice_ptr),
               "Data.b != slice[1]");
    assert_eq!(as_u8_ptr(&data.c).offset_from(data_ptr),
               as_u8_ptr(&slice[2]).offset_from(slice_ptr),
               "Data.c != slice[2]");
}

#[test]
fn test_some_offsets() {
    unsafe {
        test_slice_data_equiv(&0_u32);
        test_slice_data_equiv(&0_u64);
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM