Как обернуть заемную стоимость в новый тип, который также является заемной стоимостью? - PullRequest
0 голосов
/ 02 января 2019

Я пытаюсь использовать шаблон newtype для переноса ранее существовавшего типа.Этот внутренний тип имеет метод modify, который позволяет нам работать с заимствованным изменяемым значением в обратном вызове:

struct Val;

struct Inner(Val);

impl Inner {
    fn modify<F>(&self, f: F)
    where F: FnOnce(&mut Val) -> &mut Val { … }
}

Теперь я хочу предоставить очень похожий метод для моего нового типа Outer, который, однако, долженне работает на Val с, но опять-таки обертка нового типа WrappedVal:

struct Outer(Inner);
struct WrappedVal(Val);

impl Outer {
    fn modify<F>(&self, f: F)
    where
        F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
    {
        self.0.modify(|v| f(/* ??? */));
    }
}

Этот код является сокращенным примером из исходного API.Я не знаю, почему ссылка возвращается из замыкания, возможно, для облегчения создания цепочки, но в этом нет необходимости.Требуется &self, потому что он использует внутреннюю изменчивость - это тип, представляющий периферийный регистр во встроенной системе

Как получить &mut WrappedVal от &mut Val?

Я пробовал разные вещи, но все были разорены заемщиком.Я не могу переместить Val из изменяемой ссылки для создания правильного WrappedVal, и я не могу получить время жизни для компиляции, когда экспериментирую с struct WrappedVal(&'? mut Val) (что мне на самом деле не нужно, так как ониусложнение реализации черты).

Я в конечном итоге получил его для компиляции (см. Демонстрация Rust Playground ), используя абсолютный ужас

self.0.modify(|v| unsafe {
    (f((v as *mut Val as *mut WrappedVal).as_mut().unwrap()) as *mut WrappedVal as *mut Val)
        .as_mut()
        .unwrap()
});

, но наверняка должен бытьлучше?

1 Ответ

0 голосов
/ 02 января 2019

Не существует безопасного способа с вашим текущим определением, и ваш небезопасный код не гарантированно безопасен.Нет договора, что макет WrappedVal совпадает с макетом Val, хотя это все, что у него есть.

Решение, не использующее unsafe

Не делайте этого.Вместо этого оберните ссылку:

struct WrappedVal<'a>(&'a mut Val);

impl Outer {
    fn modify<F>(&self, f: F)
    where
        F: FnOnce(WrappedVal) -> WrappedVal,
    {
        self.0.modify(|v| f(WrappedVal(v)).0)
    }
}

Решение с использованием unsafe

Вы можете утверждать, что ваш тип имеет то же представление , что и тип, который он переносит, делаяуказатели, совместимые с помощью repr(transparent):

#[repr(transparent)]
struct WrappedVal(given::Val);

impl Outer {
    fn modify<F>(&self, f: F)
    where
        F: FnOnce(&mut WrappedVal) -> &mut WrappedVal,
    {
        self.0.modify(|v| {
            // Insert documentation why **you** think this is safe
            // instead of copy-pasting from Stack Overflow
            let wv = unsafe { &mut *(v as *mut given::Val as *mut WrappedVal) };
            let wv = f(wv);
            unsafe { &mut *(wv as *mut WrappedVal as *mut given::Val) }
        })
    }
}

При наличии repr(transparent) два указателя являются взаимозаменяемыми.Я провел быстрый тест с Мири и вашим полным примером и не получил никаких ошибок, но это не серебряная пуля, потому что я ничего не испортил.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...