Быть Sized
- это свойство типа, а не какого-либо конкретного значения. [u8]
и [T]
в общем случае не имеют размера. Однако вы можете ошибочно попытаться преобразовать в размерный тип.
let data: [u8; size_of::<Header>()] = buffer[0..size_of::<Header>()].try_into().unwrap();
try_into
обычно возвращает результат, но, поскольку мы знаем, что размер правильный, мы можем просто unwrap
его. (площадка)
// The trait `TryInto` isn't in the prelude,
// so we have to import it to use its methods.
use std::convert::TryInto;
use std::mem::{size_of, transmute};
#[derive(Debug)]
#[repr(C)]
struct Header {
// some file header
magic: u32,
data1_len: u32,
data2_len: u32,
}
fn main() {
let buffer: Vec<u8> = vec![
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
]; // whatever
let header: Header = {
let data: [u8; size_of::<Header>()] = buffer[0..size_of::<Header>()].try_into().unwrap();
unsafe { transmute(data) }
};
}
Примечание: этот шаг работает только потому, что [T; N]
реализует Copy
, если T
реализует Copy
. Мы копируем данные из вектора. Если это не то, что вам нужно, вам нужно удалить данные из вектора, используя Vec::splice
или аналогичный. Они не могут оба владеть этими данными.
Вы могли бы также рассмотреть возможность получения ссылки на Header
, которая на самом деле должна работать здесь, не копируя что-либо или изменяя вектор. Я бы избежал дикой небезопасности transmute
и использовал бы (чуть менее небезопасный) приведение необработанных указателей.
К сожалению, очевидная вещь, которую нужно сделать здесь, не работает, так как выравнивание Header
4 байта, но выравнивание [u8]
составляет только один байт. Приведение указателя от &[u8]
к &Header
может вызвать ошибку выравнивания. Причина, по которой не было проблем с [u8; _]
до Header
, заключалась в том, что transmute
принимает массив по значению и возвращает правильно выровненное значение. См. запрос на получение # 38670 .
Один из способов обойти это - сделать Header
repr(C, packed)
, что приводит к тому, что его выравнивание будет равным одному байту. Это может сделать все немного медленнее в целом (но помните: тест в первую очередь!). Я вполне уверен, что это вызывает больше проблем, чем стоит из-за необоснованности ссылок на структуры packed
, поэтому будьте осторожны с этим. (детская площадка)
use std::mem::size_of;
// We can't derive anything unless we also derive Copy
// one of the downsides of this approach
#[repr(C, packed)]
struct Header {
// some file header
magic: u32,
data1_len: u32,
data2_len: u32,
}
fn main() {
let buffer: Vec<u8> = vec![
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
1, 2, 3, 4, 5, 6, 7, 8, 9, 0,
]; // whatever
let header: &Header = unsafe { &*(buffer[0..size_of::<Header>()].as_ptr() as *const Header) };
}