У меня есть черта, которая имеет функцию для десериализации ассоциированного типа. Однако этот связанный тип должен иметь время жизни, которое решает вызывающая сторона, поэтому у меня есть отдельная черта, для которой я использую черту с более высоким рейтингом, чтобы ее можно было десериализовать для любого времени жизни.
Мне нужно использовать замыкание, которое возвращает этот связанный тип.
У меня есть следующий код для этого:
#![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
, который я возвращаю из закрытия, но Rust не думает, что они одного типа. Я предполагаю, что это потому, что Rust выбирает несовместимые анонимные времена жизни для MyEndpointBody
типа, но я не знаю, как это исправить.
Как я могу заставить этот код работать так, чтобы я мог использовать замыкание с типом, связанным с HRTB?
источник
Fn
как параметр должен иметь произвольное время жизни. Но здесь это время жизни становится зависимым и делает невозможным использование этого вида, пожалуйста, проверьте: play.rust-lang.org/…Определить
DeserializeBody
как:Out
является объявлением универсального типа. Не объявляйте время жизни здесь, это будет явно на сайте определения.На этом этапе больше нет необходимости в Признаке Высшего Ранга для
Endpoint
:На сайте определения требования времени жизни должны быть выражены для связанного типа
Out
. ЕслиDeserializeBody
не является более универсальным, тоMyEndpoint
должно быть:И чтобы реализовать такое требование, давайте прибегнем к фантомному типу, который требует жизни
'a
.Собираем все кусочки вместе:
источник
MyEndpointBody
не может заимствоватьraw_body
в этом случае, потому что'a
переживаетraw_body
анонимную жизнь. Вся точка HRTB, чтобы датьraw_body
на'a
всю жизнь.Vec<u8>
должно быть где-то выделено: перемещается вниз вdeserialize
.Я думаю, что проблема заключается в том, что вы запрашиваете, чтобы ваши обработчики могли обрабатывать все возможные времена жизни с этим ограничением HK - что компилятор не может доказать, что оно проверено, поэтому не может сделать эквивалентность
MyEndpointBody <=> MyEndpoint::Out
.Если вместо этого вы настраиваете свои обработчики так, чтобы они занимали одно время жизни, он, по-видимому, компилируется по мере необходимости ( ссылка на игровую площадку ):
источник
for<'a> Fn(&'a [u8]) -> &'a [u8]
просто отлично, и компилятор примет это. Проблема возникает только тогда, когда возвращается связанный тип.FnHandler
принимает функцию, которая для каждого возможного времени жизни что-то возвращает. В вашем случае бывает, что для любого времени жизни'a
оно всегда будет одинаковым (аVec<u8>
), но если вы этого не знали, этот результат может зависеть от времени жизни,'a
параметризующего функцию. Запрос этой функции на возврат этого (возможно, зависящего от времени жизни) типа для всех времен жизни в юниверсе , возможно, и сбивает с толку компилятор: вы не можете проверить это ограничение, не «нарушая локальность» и не зная, что ваше ограничение на самом деле не зависит от времени жизни.'static
так как бы вы реализовали вещи для разных жизней?