Является ли mem :: forget (mem :: uninitialized()) определенным поведением?
В мутагене, я инъекционный различные мутации в коде. Одна вещь, которую я хотел бы мутировать, - это шаблон, if let Ok(x) = y {.. }
. Однако это представляет собой сложную задачу, так как я не могу знать тип y
- пользователь мог бы создать собственное перечисление с унарным вариантом Ok
. Я все еще могу оппортунистически мутировать его для случаев, когда у нас действительно есть Result
, тип ошибки которого реализует значение по Default
используя свойство, которое выглядит следующим упрощенным:
#![feature(specialization)]
pub trait Errorer {
fn err(self, mutate: bool) -> Self;
}
impl<X> Errorer for X {
default fn err(self, _mutate: bool) -> Self {
self
}
}
impl<T, E> Errorer for Result<T, E>
where
E: Default,
{
fn err(self, mutate: bool) -> Self {
if mutate {
Err(Default::default())
} else {
self
}
}
}
Увы, не так много ошибок, которые реализуют Default
, поэтому это не слишком полезно. Даже реализация для Result<T, Box<Error>>
даст нам больше шансов для доллара (и будет вполне возможно). Однако, учитывая, что мне все равно, что код действительно проверяет ошибку, мне интересно, могу ли я сделать общую реализацию, расширив мутацию вышеуказанного кода до
match Errorer::err(y, mutation) {
Ok(x) => { .. }
Err(x) => { mem::forget(x); }
}
и иметь err
возвращать Err(mem::uninitialized())
при мутировании - так ли это поведение безопасно? Примечание. Я возвращаю Err(mem::uninitialized())
из метода, только для mem::forget
его позже. Я не вижу, чтобы это могло паниковать, поэтому мы должны предположить, что ценность будет действительно забыта.
Является ли это определенным поведением или я должен ожидать носовых демонов?
Ответы
Ответ 1
Нет, это не определено поведение, по крайней мере, не для всех типов. (Я не могу сказать, как ваш код будет вызываться как часть мутации, поэтому я не знаю, есть ли у вас контроль над impl
типами здесь, но общий impl
делает его похожим на то, что вы этого не делаете.) Это продемонстрировано следующий фрагмент кода:
#![feature(never_type)]
use std::mem;
fn main() {
unsafe { mem::forget(mem::uninitialized::<!>()) }
}
Если вы запустите это на игровой площадке, вы увидите, что программа умирает с помощью SIGILL. Выход ASM показывает, что LLVM просто оптимизировал всю программу до немедленного SIGILL из-за того, как он использует значение нежилого типа !
:
playground::main:
ud2
Вообще говоря, почти невозможно правильно использовать mem::uninitialized
в общем коде, см., Например, эту проблему rc::Weak
. По этой причине эта функция находится в процессе устаревания и замены. Но это вам не поможет; то, что вы хотите сделать, просто незаконно для Result<T, !>
.