2016-12-13 18 views
6

Như tôi đã hiểu, [[T; 4]; 3][T; 12] có cùng bố cục trong bộ nhớ. Cách tốt nhất để chuyển đổi giá trị giữa các loại này là gì? Tôi có thể chuyển đổi một tham chiếu thành một tham chiếu sang tham chiếu khác không? Tôi có thể tránh sao chép tất cả các yếu tố không? Tôi có cần unsafe không?Cách tốt nhất để chuyển đổi [[T; 4]; 3] thành [T; 12]?

Trả lời

9

Có, bạn có thể chuyển đổi tham chiếu thành [[T; 4]; 3] thành tham chiếu đến [T; 12], nhưng chỉ với mã không an toàn, sử dụng mem::transmute. Tốt nhất nên bọc nó trong một hàm để tham chiếu kết quả được gán cho tuổi thọ thích hợp, vì nếu không thì transmute sẽ làm cho nó có thể có được một tham chiếu có tuổi thọ lớn hơn so với tham chiếu nên có.

fn convert<'a>(a: &'a [[u8; 4]; 3]) -> &'a [u8; 12] { 
    unsafe { std::mem::transmute(a) } 
} 

Điều này có thể được rút ngắn nhờ vào các quy tắc cuộc đời sự bỏ bớt:

fn convert(a: &[[u8; 4]; 3]) -> &[u8; 12] { 
    unsafe { std::mem::transmute(a) } 
} 

Mặc dù khi giao dịch với mã không an toàn, tôi muốn hiểu nếu bạn ưa thích các phiên bản rõ ràng hơn!

+1

Chỉ cần làm rõ: Điều này thực hiện một bản sao bit-bit nếu chính tham chiếu _ chứ không phải nội dung của mảng? Tôi không chắc lắm. –

+3

@Simon: Nếu bạn chuyển một _reference_ thành mảng, thì chỉ tham chiếu được sao chép. Nếu bạn truyền trực tiếp _array_ (như bạn đã làm trong câu trả lời của bạn), thì toàn bộ mảng được sao chép. Tuy nhiên, trình biên dịch có thể bỏ qua bản sao tùy thuộc vào những gì bạn làm với giá trị và cách bạn biên dịch mã (gỡ lỗi hoặc phát hành). –

+0

Tuyệt vời không có vấn đề gì. Tôi sẽ để lại câu trả lời của tôi như là một điểm tham khảo cho bản thân mình trong tương lai. Cảm ơn bạn đã làm rõ :) –

3

Tuyên bố từ chối trách nhiệm: Tôi không thực sự tuyệt vời với mặt cấp thấp của Rust, tôi không biết điều gì được coi là "thực hành tốt" ở mức độ thấp Rust. Lời khuyên đưa ra ở đây có thể không phải là ý tưởng hay. Tôi đặt chúng ở đây mặc dù vì ... tốt, chúng hoạt động.

You could transmute them. Vấn đề là nó sẽ là một bản sao, tài liệu nói rằng nó tương đương với một cuộc gọi memcpy. Đây không phải là những gì bạn muốn, nhưng ở đây nó là anyway:

fn main() { 
    let a: [[u8; 4]; 3] = [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]; 
    let b: [u8; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; 

    println!("a: {:?}", a); 
    println!("b: {:?}", b); 

    let c = unsafe { std::mem::transmute::<[[u8; 4]; 3], [u8; 12]>(a) }; 

    println!("c: {:?}", c); 
} 

tùy chọn khác của bạn is to work with a raw pointer:

fn main() { 
    let a: [[u8; 4]; 3] = [[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]; 
    let b: [u8; 12] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]; 

    println!("a: {:?}", a); 
    println!("b: {:?}", b); 

    let c = &a as *const _ as *const [u8; 12]; 
    // Or it can be this: let c = &mut a as *mut [[u8; 4]; 3]; 

    for i in 0..12 { 
     let p = c as *const u8; 
     let v = unsafe { *p.offset(i) }; 
     println!("{}", v); 
    } 
} 

mà không phải là đặc biệt lớn, hoặc.

Con trỏ cũng có thể là một con trỏ hoặc con trỏ có thể thay đổi để cùng loại (kể từ &mut T có thể được đúc để *mut T), và các mã trên hoạt động giống hệt nhau (với a đánh dấu có thể thay đổi):

let c = &mut a as *mut [[u8; 4]; 3]; 

Tôi tự hỏi liệu đây có phải là một chút vấn đề XY hay không. Có lẽ cách bạn đang làm việc với dữ liệu của bạn có thể được thay đổi để không yêu cầu điều này?

Các vấn đề liên quan