Является ли mem :: Forgot (mem :: uninitialized ()) определенным поведением? - PullRequest
0 голосов
/ 05 мая 2018

В Мутаген , я вводлю различные мутации в коде. Одна вещь, которую я хотел бы изменить, это шаблон if let Ok(x) = y { .. }. Тем не менее, это представляет собой довольно сложную задачу, так как я не может знать тип y - пользователь мог создать свой собственный enum с одинарный 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 Ответ

0 голосов
/ 07 мая 2018

Нет, это не определенное поведение, по крайней мере, не для всех типов. (Я не могу сказать, как ваш код будет вызываться как часть мутации, поэтому я не знаю, имеете ли вы здесь контроль над типами, но универсальный 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, !>.

...