Як повернути асоційований тип із присвоєної рисою вищої категорії?


11

У мене є ознака, яка має функцію десеріалізації асоційованого типу. Однак цей асоційований тип повинен мати тривалість життя, яку вирішує абонент, тому у мене є окрема ознака, для якої я використовую присвоєну рису вищого рангу, щоб її можна було дезаріалізувати протягом будь-якого життя.

Мені потрібно використовувати закриття, яке повертає цей асоційований тип.

У мене є такий код для цього:

#![allow(unreachable_code)]

use std::marker::PhantomData;

trait Endpoint: for<'a> EndpointBody<'a> {}
trait EndpointBody<'a> {
    type Out: 'a;
    fn serialize(body: &Self::Out) -> Vec<u8>;
    fn deserialize(raw_body: &'a [u8]) -> Self::Out;
}

// /////////////////////////////////////////////////////////

/// Trait object compatible handler
trait Handler {
    fn execute(&self, raw_body: &[u8]) -> Vec<u8>;
}

/// Wraps a function for an endpoint, convertint it to a Handler
struct FnHandler<EP, F>
where
    EP: Endpoint,
    F: 'static + for<'a> Fn(&'a [u8]) -> <EP as EndpointBody<'a>>::Out,
{
    func: F,
    _ph: PhantomData<EP>,
}
impl<EP, F> FnHandler<EP, F>
where
    EP: Endpoint,
    F: 'static + for<'a> Fn(&'a [u8]) -> <EP as EndpointBody<'a>>::Out,
{
    pub fn new(func: F) -> Self {
        Self {
            func,
            _ph: PhantomData,
        }
    }
}
impl<EP, F> Handler for FnHandler<EP, F>
where
    EP: Endpoint,
    F: 'static + for<'a> Fn(&'a [u8]) -> <EP as EndpointBody<'a>>::Out,
{
    fn execute(&self, in_raw_body: &[u8]) -> Vec<u8> {
        let body = (self.func)(in_raw_body);
        let serialized_body = unimplemented!();
        return serialized_body;
    }
}

// /////////////////////////////////////////////////////////

/// Collection of handlers
struct Handlers(Vec<Box<dyn Handler>>);
impl Handlers {
    pub fn new() -> Self {
        Self(vec![])
    }

    pub fn handle<EP: 'static, F>(&mut self, func: F)
    where
        EP: Endpoint,
        F: 'static + for<'a> Fn(&'a [u8]) -> <EP as EndpointBody<'a>>::Out,
    {
        self.0.push(Box::new(FnHandler::<EP, F>::new(func)));
    }
}

// /////////////////////////////////////////////////////////

struct MyEndpoint;
struct MyEndpointBody<'a> {
    pub string: &'a str,
}
impl Endpoint for MyEndpoint {}
impl<'a> EndpointBody<'a> for MyEndpoint {
    type Out = MyEndpointBody<'a>;

    fn serialize(body: &Self::Out) -> Vec<u8> {
        unimplemented!()
    }
    fn deserialize(raw_body: &'a [u8]) -> Self::Out {
        unimplemented!()
    }
}

// /////////////////////////////////////////////////////////

fn main() {
    let mut handlers = Handlers::new();
    handlers.handle::<MyEndpoint, _>(|_body| MyEndpointBody {
        string: "test string",
    });

    handlers.0[1].execute(&[]);
}

Я думаю, що це має спрацювати, але коли я перевіряю, я отримую помилку типу:

error[E0271]: type mismatch resolving `for<'a> <[closure@src/main.rs:92:38: 94:6] as std::ops::FnOnce<(&'a [u8],)>>::Output == <MyEndpoint as EndpointBody<'a>>::Out`
  --> src/main.rs:92:14
   |
92 |     handlers.handle::<MyEndpoint, _>(|_body| MyEndpointBody {
   |              ^^^^^^ expected struct `MyEndpointBody`, found associated type
   |
   = note:       expected struct `MyEndpointBody<'_>`
           found associated type `<MyEndpoint as EndpointBody<'_>>::Out`
   = note: consider constraining the associated type `<MyEndpoint as EndpointBody<'_>>::Out` to `MyEndpointBody<'_>`
   = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

Це заплутано, тому що MyEndpoint::Outце - те MyEndpointBody, що я повертаюсь із закриття, але Раст не вважає, що вони одного типу. Я здогадуюсь, що це тому, що Руст вибирає несумісні анонімні терміни життя для MyEndpointBodyтипу, але я не знаю, як це виправити.

Як змусити цей код працювати так, щоб я міг використовувати закриття з типом, пов’язаним з HRTB?

Відповіді:


4

Обгортання типу повернення в новотипі виправляє проблему:

#![allow(unreachable_code)]

use std::marker::PhantomData;

trait Endpoint: for<'a> EndpointBody<'a> {}
trait EndpointBody<'a> {
    type Out: 'a;
    fn serialize(body: &Self::Out) -> Vec<u8>;
    fn deserialize(raw_body: &'a [u8]) -> Self::Out;
}

struct EPOut<'a, EP: Endpoint>(<EP as EndpointBody<'a>>::Out);

// /////////////////////////////////////////////////////////

/// Trait object compatible handler
trait Handler {
    fn execute(&self, raw_body: &[u8]) -> Vec<u8>;
}

/// Wraps a function for an endpoint, convertint it to a Handler
struct FnHandler<EP, F>
where
    EP: Endpoint,
    F: 'static + for<'a> Fn(&'a [u8]) -> EPOut<'a, EP>,
{
    func: F,
    _ph: PhantomData<EP>,
}
impl<EP, F> FnHandler<EP, F>
where
    EP: Endpoint,
    F: 'static + for<'a> Fn(&'a [u8]) -> EPOut<'a, EP>,
{
    pub fn new(func: F) -> Self {
        Self {
            func,
            _ph: PhantomData,
        }
    }
}
impl<EP, F> Handler for FnHandler<EP, F>
where
    EP: Endpoint,
    F: 'static + for<'a> Fn(&'a [u8]) -> EPOut<'a, EP>,
{
    fn execute(&self, in_raw_body: &[u8]) -> Vec<u8> {
        let body = (self.func)(in_raw_body);
        let serialized_body = unimplemented!();
        return serialized_body;
    }
}

// /////////////////////////////////////////////////////////

/// Collection of handlers
struct Handlers(Vec<Box<dyn Handler>>);
impl Handlers {
    pub fn new() -> Self {
        Self(vec![])
    }

    pub fn handle<EP: 'static, F>(&mut self, func: F)
    where
        EP: Endpoint,
        F: 'static + for<'a> Fn(&'a [u8]) -> EPOut<'a, EP>,
    {
        self.0.push(Box::new(FnHandler::<EP, F>::new(func)));
    }
}

// /////////////////////////////////////////////////////////

struct MyEndpoint;
struct MyEndpointBody<'a> {
    pub string: &'a str,
}
impl Endpoint for MyEndpoint {}
impl<'a> EndpointBody<'a> for MyEndpoint {
    type Out = MyEndpointBody<'a>;

    fn serialize(body: &Self::Out) -> Vec<u8> {
        unimplemented!()
    }
    fn deserialize(raw_body: &'a [u8]) -> Self::Out {
        unimplemented!()
    }
}

// /////////////////////////////////////////////////////////

fn main() {
    let mut handlers = Handlers::new();
    handlers.handle::<MyEndpoint, _>(|_body| EPOut(MyEndpointBody {
        string: "test string",
    }));

    handlers.0[1].execute(&[]);
}

Я спокусився сказати, що це помилка компілятора Rust, вважаючи, що новий тип повинен бути приблизно таким же, як і асоційований тип. Також, здається, є деякі ДВС, що стосуються використання пов'язаних з HRTB типів: https://github.com/rust-lang/rust/isissue/62529


0

Чи не могли б ви перевірити , що один

trait Endpoint: for<'a> DeserializeBody<'a> {}
trait DeserializeBody<'a> {
    type Out: 'a;
    fn deserialize(raw_body: &'a [u8]) -> Self::Out;
}

fn store_ep<'a, EP, F>(func: F)
where
    EP: Endpoint,
    F: 'static + Fn(&'a [u8]) -> <EP as DeserializeBody<'a>>::Out,
{
    let _ = Box::new(func);
    unimplemented!();
}

// /////////////////////////////////////////////////////////

struct MyEndpoint;
struct MyEndpointBody<'a> {
    pub string: &'a str,
}
impl Endpoint for MyEndpoint {}
impl<'a> DeserializeBody<'a> for MyEndpoint {
    type Out = MyEndpointBody<'a>;
    fn deserialize(raw_body: &'a [u8]) -> Self::Out {
        unimplemented!();
    }
}

// /////////////////////////////////////////////////////////

fn main() {
    store_ep::<MyEndpoint, _>(|raw_body| MyEndpointBody { string: "test" });
}

Це може бути не узагальненим рішенням, оскільки Fnпараметр 's повинен мати довільний термін експлуатації. Але тут це життя стає залежним, і це робить використання такого роду неможливим, будь ласка, перевірте: play.rust-lang.org/…
Ömer Erden

На жаль, хоча це працює з простим прикладом, він не працює з кодом, який я маю для свого проекту. Я оновлю свій приклад, щоб краще проілюструвати те, що я роблю.
Полковник тридцять два

0

Визначте DeserializeBodyяк:

trait DeserializeBody {
    type Out;
    fn deserialize(raw_body: &[u8]) -> Self::Out;
}

Outє декларацією родового типу. Не оголошуйте прив’язане життя, це буде явним на сайті визначення.

На даний момент більше немає необхідності, що обмежує риси вищого рангу для Endpoint:

trait Endpoint: DeserializeBody {}

trait DeserializeBody {
    type Out;
    fn deserialize(raw_body: &[u8]) -> Self::Out;
}

На місці визначення повинні бути виражені вимоги до життя для відповідного типу Out. Якщо DeserializeBodyне більше загального, тоді MyEndpointповинно бути:

impl<'a> DeserializeBody for MyEndpoint<'a> {
    type Out = MyEndpointBody<'a>;

    ...

А для реалізації такої вимоги можна вдатися до фантомного типу, який вимагає цілого життя 'a.

З'єднання всіх частин разом:

use core::marker::PhantomData;

trait Endpoint: DeserializeBody {}

trait DeserializeBody {
    type Out;
    fn deserialize(raw_body: &[u8]) -> Self::Out;
}

fn store_ep<EP, F>(func: F)
where
    EP: Endpoint,
    F: 'static + for<'a> Fn(&'a [u8]) -> <EP as DeserializeBody>::Out,
{
    let _ = Box::new(func);
    unimplemented!();
}

struct MyEndpoint<'a> {
    phantom: PhantomData<&'a ()>
}

struct MyEndpointBody<'a> {
    pub string: &'a str,
}

impl<'a> Endpoint for MyEndpoint<'a> {}

impl<'a> DeserializeBody for MyEndpoint<'a> {
    type Out = MyEndpointBody<'a>;

    fn deserialize(raw_body: &[u8]) -> Self::Out {
        unimplemented!();
    }
}

fn main() {
    store_ep::<MyEndpoint, _>(|raw_body| MyEndpointBody { string: "test" });
}

Ні. MyEndpointBodyне можу взяти позику raw_bodyв цьому випадку, тому що 'aпереживає raw_bodyанонімне життя. Вся справа в ЗГТ полягає в тому, щоб приділити raw_bodyсобі 'aвсе життя.
Полковник тридцять два

О Я бачу. За допомогою HRTB ви намагаєтеся дезаріалізувати на будь-яке життя, а після цього запозичити вхідні дані. Частина, яка здається обмеженням компілятора, виглядає як ваше рішення: serde :: DeserializeOwned і serde impl не може запозичити будь-які дані у десериалізатора.
аттдона

Чи повинен цей спосіб працювати для вас? Vec<u8>Потрібно виділити десь: переміщує вниз розподіл на deserialize.
attdona

Ну так, я міг би просто відмовитись і зняти все життя, але тоді я не можу мати десеріалізацію з нульовою копією, і це перемагає суть питання.
Полковник тридцять два

0

Я думаю, що проблема полягає в тому, що ви вимагаєте, щоб ваші обробники мали змогу обробляти всі можливі життєві періоди з тим обмеженням HK - яке компілятор не може довести, перевірено, тому не в змозі зробити еквівалентність MyEndpointBody <=> MyEndpoint::Out.

Якщо замість цього, ви параметризуєте ваших обробників на певний термін служби, схоже, компілюється як потрібно ( посилання на майданчик ):

#![allow(unreachable_code)]

use std::marker::PhantomData;

trait Endpoint: for<'a> EndpointBody<'a> {}
trait EndpointBody<'a> {
    type Out: 'a;
    fn serialize(body: &Self::Out) -> Vec<u8>;
    fn deserialize(raw_body: &'a [u8]) -> Self::Out;
}
/// Trait object compatible handler
trait Handler<'a> {
    fn execute(&self, raw_body: &'a [u8]) -> Vec<u8>;
}

/// Wraps a function for an endpoint, convertint it to a Handler
struct FnHandler<EP, F>
where
    EP: Endpoint,
    F: 'static,
{
    func: F,
    _ph: PhantomData<EP>,
}
impl<EP, F> FnHandler<EP, F>
where
    EP: Endpoint,
    F: 'static,
{
    pub fn new(func: F) -> Self {
        Self {
            func,
            _ph: PhantomData,
        }
    }
}
impl<'a, EP, F> Handler<'a> for FnHandler<EP, F>
where
    EP: Endpoint,
    F: 'static + Fn(&'a [u8]) -> <EP as EndpointBody<'a>>::Out,
{
    fn execute(&self, in_raw_body: &'a [u8]) -> Vec<u8> {
        let body = (self.func)(in_raw_body);
        let serialized_body = unimplemented!();
        return serialized_body;
    }
}

// /////////////////////////////////////////////////////////

/// Collection of handlers
struct Handlers<'a>(Vec<Box<dyn Handler<'a>>>);
impl<'a> Handlers<'a> {
    pub fn new() -> Self {
        Self(vec![])
    }

    pub fn handle<EP: 'static, F>(&mut self, func: F)
    where
        EP: Endpoint,
        F: 'static + Fn(&'a [u8]) -> <EP as EndpointBody<'a>>::Out,
    {
        self.0.push(Box::new(FnHandler::<EP, F>::new(func)));
    }
}

// /////////////////////////////////////////////////////////

struct MyEndpoint;
struct MyEndpointBody<'a> {
    pub string: &'a str,
}
impl Endpoint for MyEndpoint {}
impl<'a> EndpointBody<'a> for MyEndpoint {
    type Out = MyEndpointBody<'a>;

    fn serialize(body: &Self::Out) -> Vec<u8> {
        unimplemented!()
    }
    fn deserialize(raw_body: &'a [u8]) -> Self::Out {
        unimplemented!()
    }
}

// /////////////////////////////////////////////////////////

fn main() {
    let mut handlers = Handlers::new();
    handlers.handle::<MyEndpoint, _>(|_body| MyEndpointBody {
        string: "test string",
    });

    handlers.0[1].execute(&[]);
}

Я не розумію вашого першого абзацу. Ви можете зробити, наприклад, for<'a> Fn(&'a [u8]) -> &'a [u8]просто чудово, і компілятор прийме це. Саме тоді, коли пов'язаний тип повертається, це викликає проблему.
Полковник тридцять два

Я мав на увазі, що ваш FnHandlerприймає функцію, яка протягом кожного можливого життя щось повертає. У вашому випадку трапляється так, що протягом будь-якого періоду життя 'aвін завжди буде однаковим (а Vec<u8>), але якщо ви цього не знали, цей висновок може залежати від періоду експлуатації, який 'aпараметризує функцію. Попросити цю функцію повернути цей (можливо, залежний від життя) тип протягом усього життя у Всесвіті , можливо, те, що бентежить компілятор: ви не можете перевірити це обмеження, не «порушуючи місцевість» і знаючи, що ваше обмеження насправді не залежить від життя.
val

Це не так, оскільки обробка обгортки нового типу у моїй відповіді працює чудово під час використання відповідного типу. Я не думаю, що ти можеш навіть мати різні асоційовані типи протягом різного життя; Єдиний названий термін експлуатації, доступний в глобальній області, де вам потрібно поставити impls, - 'staticтак як би ви реалізували речі для різних термінів життя?
Полковник тридцять два
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.