簡體   English   中英

是否可以在 Rust 中使用指針訪問結構的元素?

[英]Is it possible to access elements of a struct using pointers in 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
}

並通過它們的索引訪問每個元素。

在一般情況下,今天在 Rust 中沒有辦法正確地做到這一點。 但是,您的特定結構避免了一些最嚴重的問題,從而可以安全地將整個結構作為借用切片 ( &[T] ) 借用。 為此,您需要做三件事:

  1. 標記 struct repr(C)但不標記repr(packed) 壓縮結構是未對齊的,並且引用必須始終正確對齊。

  2. 檢查結構的大小是否不大於isize::MAX

  3. 使用slice::from_raw_parts&Data<T>借用&[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) }
    }
}

這是一個測試函數,用於增加對切片進行轉換的信心。

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