Ich habe eine Eigenschaft, die eine Funktion zum Deserialisieren eines zugeordneten Typs hat. Dieser zugeordnete Typ muss jedoch eine Lebensdauer haben, über die der Anrufer entscheidet. Daher habe ich ein separates Merkmal, für das ich ein höherrangiges Merkmal verwende, damit es für jedes Leben deserialisiert werden kann.
Ich muss einen Abschluss verwenden, der diesen zugeordneten Typ zurückgibt.
Ich habe den folgenden Code, um das zu tun:
#![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(&[]);
}
Ich denke, das sollte funktionieren, aber wenn ich es überprüfe, erhalte ich einen Tippfehler:
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
Es ist verwirrend, weil MyEndpoint::Out
es eine ist MyEndpointBody
, die ich von der Schließung zurückkehre, aber Rust glaubt nicht, dass sie vom gleichen Typ sind. Ich vermute, das liegt daran, dass Rust inkompatible anonyme Lebensdauern für den MyEndpointBody
Typ auswählt, aber ich weiß nicht, wie ich das beheben soll.
Wie kann ich diesen Code zum Laufen bringen, damit ich einen Abschluss mit einem HRTB-zugeordneten Typ verwenden kann?
quelle
Fn
der Parameter eine beliebige Lebensdauer haben muss. Aber hier wird dieses Leben abhängig und es macht diese Art einer Verwendung unmöglich, bitte überprüfen Sie: play.rust-lang.org/…Definieren
DeserializeBody
als:Out
ist eine Deklaration eines generischen Typs. Erklären Sie hier nicht, dass die Lebensdauer gebunden ist, dies wird auf der Definitionsseite explizit angegeben.Ab diesem Zeitpunkt ist das höherrangige Merkmal nicht mehr erforderlich für
Endpoint
:An der Definitionsstelle muss eine Lebensdaueranforderung für den zugehörigen Typ angegeben werden
Out
. WennDeserializeBody
es nicht mehr ein Generikum ist,MyEndpoint
muss es sein:Um solche Anforderungen zu implementieren, können Sie auf einen Phantomtyp zurückgreifen, der eine lebenslange Lebensdauer erfordert
'a
.Alle Teile zusammenfügen:
quelle
MyEndpointBody
kannraw_body
in diesem Fall nicht ausleihen , da die anonyme Lebensdauer'a
überlebtraw_body
. Der gesamte Punkt des HRTB ist es,raw_body
die'a
Lebensdauer anzugeben .Vec<u8>
muss irgendwo zugeordnet werden: verschiebt die Zuordnung nach unten in diedeserialize
.Ich denke, das Problem ist, dass Sie Ihre Handler auffordern, alle möglichen Lebensdauern mit dieser HK-Einschränkung zu bewältigen - was der Compiler nicht nachweisen kann, ist verifiziert und daher nicht in der Lage, die Äquivalenz herzustellen
MyEndpointBody <=> MyEndpoint::Out
.Wenn Sie stattdessen Ihre Handler so parametrisieren, dass sie eine einzige Lebensdauer haben, scheint sie nach Bedarf zu kompilieren ( Spielplatz-Link ):
quelle
for<'a> Fn(&'a [u8]) -> &'a [u8]
ganz gut machen, und der Compiler wird es akzeptieren. Nur wenn der zugehörige Typ zurückgegeben wird, verursacht das Problem.FnHandler
eine Funktion übernehmen, die für jedes mögliche Leben etwas zurückgibt. In Ihrem Fall ist es für jede Lebensdauer'a
immer dieselbe (aVec<u8>
). Wenn Sie dies jedoch nicht wussten, hängt diese Ausgabe möglicherweise von der Lebensdauer ab'a
, die die Funktion parametrisiert. Das Anfordern dieser Funktion, diesen (möglicherweise lebenszeitabhängigen) Typ für alle Lebensdauern im Universum zurückzugeben, verwirrt den Compiler möglicherweise: Sie können diese Einschränkung nicht überprüfen, ohne die Lokalität zu brechen und zu wissen, dass Ihre Einschränkung tatsächlich nicht lebensdauerabhängig ist.'static
Wie würden Sie also Dinge für verschiedene Lebensdauern implementieren?