2016-12-13 59 views

Odpowiedz

9

Tak, możesz zamienić odniesienie na [[T; 4]; 3] na odniesienie do [T; 12], ale tylko z niebezpiecznym kodem, używając mem::transmute. Najlepiej jest zawrzeć to w funkcji, aby wynikowy odnośnik miał przypisany odpowiedni czas życia, ponieważ w przeciwnym razie transmute umożliwiłoby uzyskanie odniesienia o dłuższym czasie życia niż powinno mieć odniesienie.

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

ten może zostać skrócony dzięki zasadom życia elizja:

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

Choć gdy ma do czynienia z niebezpiecznym kodem, chciałbym zrozumieć, jeśli preferowane bardziej wyraźny wersję!

+1

Po prostu wyjaśnić: Czy kopia bitowa jest bitowa, jeśli odwołuje się do siebie, a nie do zawartości tablicy? Nie byłem pewien. –

+3

@Simon: Jeśli transmutujesz _reference_ do tablicy, kopiowana jest tylko referencja. Jeśli transmutujesz _array_ bezpośrednio (tak jak w twojej odpowiedzi), wtedy cała tablica jest kopiowana. Jednak kompilator może przesłać kopię w zależności od tego, co zrobisz z wartością i sposobem kompilacji kodu (debugowanie lub wydanie). –

+0

Świetne bez problemu. W każdym razie zostawię swoją odpowiedź jako punkt odniesienia dla siebie w przyszłości. Dzięki za wytłumaczenie :) –

3

Nota prawna: Nie jestem naprawdę świetny w sprawie niskiego poziomu rdzy Rusta, ale nie wiem, co jest uważane za "dobrą praktykę" w rdzy niskiego poziomu. Porady tutaj podane mogą nie być dobrymi pomysłami. Wrzucam je tutaj, ponieważ ... no cóż, działają.

You could transmute them. Problem polega na tym, że będzie to kopia, dokumentacja mówi, że jest to odpowiednik połączenia memcpy. To nie jest to, czego chciał, ale tutaj jest to w każdym razie:

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); 
} 

Twój innych opcji 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); 
    } 
} 

który nie jest szczególnie wielki albo.

Wskaźnik może być również wskaźnikiem lub zmienny wskaźnik do tego samego typu (od &mut T mogą być oddane do *mut T), a powyższy kod działa dokładnie tak samo (z a oznaczone zmienny):

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

Zastanawiam się, czy jest to jednak problem XY. Być może sposób, w jaki pracujesz z danymi, może zostać zmieniony, aby tego nie wymagać?