2016-12-13 4 views
6

Как я понимаю, [[T; 4]; 3] и [T; 12] имеют одинаковый макет в памяти. Каков наилучший способ преобразования значения между этими типами? Могу ли я преобразовать ссылку на один в ссылку на другую? Могу ли я избежать копирования всех элементов? Нужно ли мне unsafe?Каков наилучший способ конвертировать [[T; 4]; 3] в [T; 12]?

ответ

9

Да, вы можете преобразовать ссылку на [[T; 4]; 3] в ссылку на [T; 12], но только с небезопасным кодом, используя mem::transmute. Лучше всего обернуть это в функцию, так что результирующей ссылке присваивается соответствующее время жизни, так как иначе transmute позволит получить ссылку с большим временем жизни, чем должна иметь ссылка.

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

Это может быть сокращен благодаря правилам срок службы Пропуска:

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

Хотя при работе с небезопасным кодом, я бы понял, если вы предпочитаете более явную версию!

+1

Просто для уточнения: это делает бит-бит для копирования, если _the reference self_, а не содержимое массива? Я не был уверен. –

+3

@Simon: Если вы преобразуете _reference_ в массив, тогда копируется только ссылка. Если вы трансформируете _array_ напрямую (как и в вашем ответе), тогда весь массив будет скопирован. Однако компилятор может удалить копию в зависимости от того, что вы делаете со значением и от того, как вы скомпилируете код (отладка или выпуск). –

+0

Отличная проблема. В любом случае я оставлю свой ответ в качестве ориентира для себя в будущем. Спасибо за разъяснения :) –

3

Отказ от ответственности: Я не очень хорошо разбираюсь в отношениях с Rust на низком уровне вещей, но я не знаю, что считается «хорошей практикой» в русле ржавого уровня. Рекомендации, приведенные здесь, могут не быть хорошими идеями. Я помещаю их сюда, хотя ... ну, они работают.

You could transmute them. Проблема в том, что это будет копия, в документации говорится, что это эквивалент вызова memcpy. Это не то, что вы хотели, но здесь это все равно:

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

Другой вариант 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); 
    } 
} 

, не особенно велика либо.

Указатель также может быть указателем или изменяемым указатель на тот же тип (с &mut T может быть приведена к *mut T), и приведенному выше код работает точно так же (с a отмечен изменяемым):

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

Я действительно задаюсь вопросом, является ли это немного проблемой XY. Может быть, способ, которым вы работаете с вашими данными, может быть изменен, чтобы не требовать этого?