Как определить размер массива во время компиляции в Rust? - PullRequest
4 голосов
/ 27 октября 2019

У меня есть библиотека C, которая ожидает строковый тип, который явно определяет длину строки:

#[repr(C)]
pub struct FFIStr {
    len: usize,
    data: *const u8,
}

Поскольку этот тип используется как статический, я хотел бы получить способ безопасно объявить его, используя constфункция или макрос (вместо ручной настройки len).

Моей первой попыткой было использование макроса и len(), однако в версиях до 1.39.0 это невозможночтобы получить длину среза как константу fn :

macro_rules! ffi_string {
    ($x:expr) => {
        FFIStr { len: $x.len(), data: $x as *const u8 }
    };
}

#[no_mangle]
pub static mut HELLO_WORLD: FFIStr = ffi_string!(b"Hello, world!");

error: core::slice::<impl [T]>::len` is not yet stable as a const function

Моя вторая попытка была использовать std::mem::size_of<T>, но, похоже, нет способа получить тип статического массива за исключением использования обобщений:

const fn ffi_string<T>(s: &'static T) -> FFIStr {
    FFIStr { len: ::std::mem::size_of::<T>(), data: s as *const _ as *const _ }
}

#[no_mangle]
pub static mut HELLO_WORLD: FFIStr = ffi_string(b"Hello, world!");

Хотя это работает (удивительно), оно ужасно склонно к неправильному использованию, так как оно дико преобразуетсячто бы вы ни передавали в *const u8.

Кажется, что const_generics было бы хорошим решением для этого, но в настоящее время они нестабильны:

const fn ffi_string<const SIZE: usize>(s: &'static [u8; SIZE]) -> FFIStr {
    FFIStr { len: SIZE, data: s as *const u8 }
}

#[no_mangle]
pub static mut X: FFIStr = ffi_string(b"Hello, world!");

error[E0658]: const generics are unstable

Есть ли лучший способ определить размер статического массива во время компиляции?

1 Ответ

2 голосов
/ 31 октября 2019

В Rust 1.39.0 [T]::len было стабилизировано как постоянная функция , теперь это делается прямо:

const ARRAY: [i32; 3] = [1, 2, 3];
const ARRAY_SIZE: usize = ARRAY.len();

fn main() {
    assert_eq!(3, ARRAY_SIZE);
}

В ранееВерсии Rust, вот один способ, основанный на общем макросе C ARRAY_SIZE:

macro_rules! array_size {
    ($x:expr) => (
        (size_of_val($x) / size_of_val(&$x[0]))
    )
}

const fn size_of_val<T>(_: &T) -> usize {
    std::mem::size_of::<T>()
}

fn main() {
    assert_eq!(3, array_size!(&[1, 2, 3]));
    assert_eq!(13, array_size!(b"Hello, world!"));
}

Он использует универсальную функцию const size_of_val<T> для определения типа и, следовательно, размера значения, передаваемого по ссылке (встроенный std::mem::size_of_val не является постоянным).

Примечание : это не работает для массивов размера 0. Это можно исправить с помощьюsize_of_val($x) / size_of_val(unsafe { &*$x.as_ptr() }) за счет ошибочного принятия типов, не являющихся массивами (например, &String).

...