[英]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]
) 借用。 為此,您需要做三件事:
標記 struct repr(C)
,但不標記repr(packed)
! 壓縮結構是未對齊的,並且引用必須始終正確對齊。
檢查結構的大小是否不大於isize::MAX
。
使用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.