首页 > 解决方案 > 是否可以使用 Rust 中的指针访问结构的元素?

问题描述

在 C 中,我们可以通过指针访问结构的各个元素。我们如何在 Rust 中做同样的事情?

下面的代码展示了如何使用 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;
}

以上是使用编译的gcc,我知道它也是特定于平台的,但这不是我关心的。

以下是我目前在 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());
}

我希望能够使用

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

并通过它们的索引访问每个元素。

标签: cstructrust

解决方案


在一般情况下,今天在 Rust 中没有办法正确地做到这一点。但是,您的特定结构避免了一些最严重的问题,使得将整个结构作为借用切片 ( &[T]) 借用是安全的。为此,您需要做三件事:

  1. 标记结构repr(C)但不是repr(packed)!打包结构是未对齐的,并且引用必须始终正确对齐。

  2. 检查结构的大小不大于isize::MAX.

  3. 用于从slice::from_raw_parts中借 a 。&[T]&Data<T>

有关为什么这是合理的逐点证明,请参阅将结构强制转换为数组是否合法?

#[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) }
    }
}

推荐阅读