Есть ли "безопасный" способ выполнить такую интерпретацию данных?
Нет. По крайней мере, это потому, что информация, которую вам нужно знать, не выражается в системе типов Rust, а выражается в прозе (a.k.a. docs):
Complex<T>
совместима с массивом памяти с массивом [T; 2]
.
- Complex
документы
Если Vec
выделил память, то [...] его указатель указывает на len
инициализированных смежных элементов по порядку (что вы увидите, если вы приведете его к срезу),
- Vec
документы
Массивы приводят к кусочкам ([T]
)
- Array docs
Поскольку Complex
совместим с памятью с массивом, данные массива совместимы с срезом по памяти, а данные Vec
совместимы с срезом по памяти, это преобразование должно быть безопасным, даже хотя компилятор не может сказать это.
Эта информация должна быть прикреплена (через комментарий) к вашему небезопасному блоку.
Я сделает небольшие изменения в вашей функции:
Наличие двух Vec
с одновременно указывающих на одни и те же данные заставляет меня очень нервничать. Этого можно легко избежать, введя некоторые переменные и забыв один перед созданием другого.
Удалите ключевое слово return
для большей идиоматичности
Добавьте некоторые утверждения, что начальная длина данных кратна двум.
Как указывает Родриго , емкость может легко быть нечетным числом. Чтобы попытаться избежать этого, мы вызываем shrink_to_fit
. Это имеет недостаток в том, что Vec
может необходимо перераспределить и скопировать память, в зависимости от реализации.
Разверните блок unsafe
, чтобы охватить весь связанный код, необходимый для обеспечения соблюдения защитных инвариантов.
pub fn convert_to_complex(mut buffer: Vec<f64>) -> Vec<num_complex::Complex<f64>> {
// This is where I'd put the rationale for why this `unsafe` block
// upholds the guarantees that I must ensure. Too bad I
// copy-and-pasted from Stack Overflow without reading this comment!
unsafe {
buffer.shrink_to_fit();
let ptr = buffer.as_mut_ptr() as *mut num_complex::Complex<f64>;
let len = buffer.len();
let cap = buffer.capacity();
assert!(len % 2 == 0);
assert!(cap % 2 == 0);
std::mem::forget(buffer);
Vec::from_raw_parts(ptr, len / 2, cap / 2)
}
}
Чтобы не беспокоиться о емкости, вы можете просто преобразовать срез в Vec
. Это также не имеет никакого дополнительного выделения памяти. Это проще, потому что мы можем «потерять» любые нечетные конечные значения, потому что Vec
все еще поддерживает их.
pub fn convert_to_complex(buffer: &[f64]) -> &[num_complex::Complex<f64>] {
// This is where I'd put the rationale for why this `unsafe` block
// upholds the guarantees that I must ensure. Too bad I
// copy-and-pasted from Stack Overflow without reading this comment!
unsafe {
let ptr = buffer.as_ptr() as *mut num_complex::Complex<f64>;
let len = buffer.len();
std::slice::from_raw_parts(ptr, len / 2)
}
}