Jak zwrócić powiązany typ z cechy związanej z wyższą rangą cechy?


11

Mam cechę, która ma funkcję deserializacji powiązanego typu. Jednak ten typ skojarzony musi mieć okres istnienia, który decyduje osoba dzwoniąca, więc mam osobną cechę, dla której używam cechy wyższego rzędu, aby można ją było zdezrializować na dowolny okres.

Muszę użyć zamknięcia, które zwraca ten skojarzony typ.

Mam do tego następujący kod:

#![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(&[]);
}

Myślę, że to powinno działać, ale kiedy to sprawdzam, pojawia się błąd typu:

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

To mylące, ponieważ MyEndpoint::Outto MyEndpointBody, co wracam z zamknięcia, ale rdza nie myślą, że są tego samego typu. Domyślam się, że to dlatego, że Rust wybiera niekompatybilne anonimowe czasy życia dla tego MyEndpointBodytypu, ale nie wiem, jak to naprawić.

Jak mogę uruchomić ten kod, aby móc użyć zamknięcia z typem powiązanym z HRTB?

Odpowiedzi:


4

Zamknięcie zawinięcia typu zwrotu w nowy typ rozwiązuje problem:

#![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(&[]);
}

Kusi mnie, aby powiedzieć, że jest to błąd kompilatora Rust, biorąc pod uwagę, że nowy typ powinien być prawie taki sam jak typ skojarzony. Wydaje się również, że istnieją pewne ICE związane z używaniem typów powiązanych z HRTB: https://github.com/rust-lang/rust/issues/62529


0

Czy mógłbyś sprawdzić , że jeden

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" });
}

To może nie być uogólnione rozwiązanie, ponieważ Fnparametr musi mieć dowolny okres istnienia. Ale tutaj to życie staje się zależne i sprawia, że ​​korzystanie z tego rodzaju jest niemożliwe, sprawdź: play.rust-lang.org/…
Ömer Erden

Niestety, chociaż działa to na prostym przykładzie, nie działa z kodem, który mam dla mojego projektu. Zaktualizuję mój przykład, aby lepiej zilustrować to, co robię.
Pułkownik Trzydzieści Dwa

0

Zdefiniuj DeserializeBodyjako:

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

Outjest deklaracją typu ogólnego. Nie deklaruj tutaj dożywotniego ograniczenia, będzie to jawne w witrynie definicji.

W tym momencie nie jest już konieczne ograniczenie cechy wyższej rangi do Endpoint:

trait Endpoint: DeserializeBody {}

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

W miejscu definicji należy podać wymagania dotyczące okresu użytkowania dla powiązanego typu Out. Jeśli DeserializeBodynie jest bardziej ogólny, to MyEndpointmusi być:

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

    ...

Aby wdrożyć taki wymóg, należy zastosować typ fantomowy, który wymaga życia 'a.

Złożenie wszystkich elementów razem:

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" });
}

Nie. MyEndpointBodyNie można pożyczyć od raw_bodyw tym przypadku, ponieważ 'ażyją dłużej raw_bodyanonimowego życia „s. Cały punkt HRTB jest dać raw_bodysię 'acałe życie.
Pułkownik Trzydzieści Dwa

Rozumiem. Z HRTB próbujesz dokonać deserializacji na dowolny okres życia, a następnie pożyczyć od danych wejściowych. Część, która wydaje się ograniczeniem kompilatora, która wygląda na twoje rozwiązanie, to serde :: DeserializeOwned, a serde impl nie może pożyczyć żadnych danych z deserializera.
attdona

Czy to obejście powinno działać dla Ciebie? Vec<u8>musi być gdzieś przydzielony: przenosi alokację w dół do deserialize.
attdona

Cóż, tak, mógłbym po prostu zrezygnować i usunąć czas życia, ale wtedy nie mogę mieć deserializacji z zerowym kopiowaniem i to nie pozwala na sedno pytania.
Pułkownik Trzydzieści Dwa

0

Myślę, że problem polega na tym, że żądasz od swoich programów obsługi, aby mogły obsługiwać wszystkie możliwe okresy istnienia z tym ograniczeniem HK - czego kompilator nie może udowodnić, jest zweryfikowany, a zatem nie jest w stanie dokonać równoważności MyEndpointBody <=> MyEndpoint::Out.

Jeśli zamiast tego sparametryzujesz swoje procedury obsługi, aby trwały tylko jeden okres życia, wydaje się, że kompilują się zgodnie z wymaganiami ( link do placu zabaw ):

#![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(&[]);
}

Nie rozumiem twojego pierwszego akapitu. Możesz to zrobić na przykład for<'a> Fn(&'a [u8]) -> &'a [u8]dobrze, a kompilator to zaakceptuje. Problem pojawia się dopiero po zwróceniu powiązanego typu.
Pułkownik Trzydzieści Dwa

Miałem na myśli, że FnHandlerbierzesz funkcję, która na każde możliwe życie zwraca coś. Zdarza się w twoim przypadku, że dla każdego okresu życia 'azawsze będzie on taki sam (a Vec<u8>), ale jeśli nie wiesz, że wynik może zależeć od czasu życia 'aparametryzującego funkcję. Żądanie, aby ta funkcja zwróciła ten typ (prawdopodobnie zależny od czasu życia) dla wszystkich okresów istnienia we wszechświecie, prawdopodobnie dezorientuje kompilator: nie możesz zweryfikować tego ograniczenia bez „zerwania lokalizacji” i wiedząc, że twoje ograniczenie nie jest zależne od życia.
val

To nie jest przypadek, ponieważ opakowanie typu nowego w mojej odpowiedzi działa dobrze podczas używania powiązanego typu. Nie sądzę, żebyś mógł mieć różne powiązane typy dla różnych okresów życia; jedynym nazwanym czasem życia dostępnym w zasięgu globalnym, w którym należy umieścić implanty, jest 'staticwięc, jak zaimplementować rzeczy dla różnych okresów życia?
Pułkownik Trzydzieści Dwa
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.