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::Out
to 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 MyEndpointBody
typu, ale nie wiem, jak to naprawić.
Jak mogę uruchomić ten kod, aby móc użyć zamknięcia z typem powiązanym z HRTB?
źródło
Fn
parametr 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/…Zdefiniuj
DeserializeBody
jako:Out
jest 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
:W miejscu definicji należy podać wymagania dotyczące okresu użytkowania dla powiązanego typu
Out
. JeśliDeserializeBody
nie jest bardziej ogólny, toMyEndpoint
musi być:Aby wdrożyć taki wymóg, należy zastosować typ fantomowy, który wymaga życia
'a
.Złożenie wszystkich elementów razem:
źródło
MyEndpointBody
Nie można pożyczyć odraw_body
w tym przypadku, ponieważ'a
żyją dłużejraw_body
anonimowego życia „s. Cały punkt HRTB jest daćraw_body
się'a
całe życie.Vec<u8>
musi być gdzieś przydzielony: przenosi alokację w dół dodeserialize
.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 ):
źródło
for<'a> Fn(&'a [u8]) -> &'a [u8]
dobrze, a kompilator to zaakceptuje. Problem pojawia się dopiero po zwróceniu powiązanego typu.FnHandler
bierzesz funkcję, która na każde możliwe życie zwraca coś. Zdarza się w twoim przypadku, że dla każdego okresu życia'a
zawsze będzie on taki sam (aVec<u8>
), ale jeśli nie wiesz, że wynik może zależeć od czasu życia'a
parametryzują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.'static
więc, jak zaimplementować rzeczy dla różnych okresów życia?