Какой лучший способ конвертировать [[T; 4]; 3] в [Т; 12]?

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

2 ответа

Решение

Да, вы можете преобразовать ссылку в [[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) }
}

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

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

Вы могли бы трансмутировать их. Проблема в том, что это будет копия, в документации сказано, что это эквивалент 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);
}

Другой вариант - работать с необработанным указателем:

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. Может быть, способ работы с вашими данными может быть изменен, чтобы не требовать этого?

Другие вопросы по тегам