почему размер параметра неизвестен? - PullRequest
0 голосов
/ 10 февраля 2020

Вот мой код:

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 = {
        unsafe { transmute(buffer[0..(size_of::<Header>())]) }
    };
}

но при компиляции я получаю эту ошибку:

error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
   --> main.rs:15:22
    |
15  |         unsafe { transmute(buffer[0..(size_of::<Header>())]) }
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time

На самом деле мой тип имеет размер, я говорю точный диапазон байтов, что мой Тип занимает. Почему компилятор жалуется? Он должен получить размер моего типа сам по себе. Как мне сказать компилятору, что мой размер для transmute равен размеру структуры Header?

1 Ответ

2 голосов
/ 10 февраля 2020

Быть 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) };
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...