Как я понимаю, [[T; 4]; 3]
и [T; 12]
имеют одинаковый макет в памяти. Каков наилучший способ преобразования значения между этими типами? Могу ли я преобразовать ссылку на один в ссылку на другую? Могу ли я избежать копирования всех элементов? Нужно ли мне unsafe
?Каков наилучший способ конвертировать [[T; 4]; 3] в [T; 12]?
ответ
Да, вы можете преобразовать ссылку на [[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 на низком уровне вещей, но я не знаю, что считается «хорошей практикой» в русле ржавого уровня. Рекомендации, приведенные здесь, могут не быть хорошими идеями. Я помещаю их сюда, хотя ... ну, они работают.
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. Может быть, способ, которым вы работаете с вашими данными, может быть изменен, чтобы не требовать этого?
Просто для уточнения: это делает бит-бит для копирования, если _the reference self_, а не содержимое массива? Я не был уверен. –
@Simon: Если вы преобразуете _reference_ в массив, тогда копируется только ссылка. Если вы трансформируете _array_ напрямую (как и в вашем ответе), тогда весь массив будет скопирован. Однако компилятор может удалить копию в зависимости от того, что вы делаете со значением и от того, как вы скомпилируете код (отладка или выпуск). –
Отличная проблема. В любом случае я оставлю свой ответ в качестве ориентира для себя в будущем. Спасибо за разъяснения :) –