Saya memiliki sifat yang memiliki fungsi untuk deserializing tipe terkait. Namun tipe yang terkait perlu memiliki seumur hidup yang diputuskan oleh penelepon, jadi saya memiliki sifat terpisah yang saya gunakan untuk sifat peringkat yang lebih tinggi, sehingga dapat di-deserialisasi untuk seumur hidup.
Saya perlu menggunakan penutupan yang mengembalikan tipe terkait ini.
Saya memiliki kode berikut untuk melakukan itu:
#![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(&[]);
}
Saya pikir itu harus bekerja, tetapi ketika saya memeriksanya saya mendapatkan kesalahan ketik:
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
Ini membingungkan karena MyEndpoint::Out
adalah MyEndpointBody
, yang saya kembali dari penutupan, tetapi Rust tidak berpikir mereka adalah tipe yang sama. Saya menduga itu karena Rust memilih umur anonim yang tidak kompatibel untuk MyEndpointBody
jenis ini, tapi saya tidak tahu bagaimana cara memperbaikinya.
Bagaimana saya bisa membuat kode ini berfungsi sehingga saya bisa menggunakan penutupan dengan tipe terkait HRTB?
sumber
Fn
parameter perlu memiliki masa pakai yang sewenang-wenang. Tapi di sini seumur hidup ini menjadi tergantung dan membuat penggunaan seperti ini menjadi tidak mungkin, silakan periksa: play.rust-lang.org/…Tentukan
DeserializeBody
sebagai:Out
adalah deklarasi tipe generik. Jangan menyatakan seumur hidup terikat di sini, itu akan eksplisit di situs definisi.Pada titik ini tidak diperlukan lagi Bound Sifat Tinggi untuk
Endpoint
:Di situs definisi, persyaratan seumur hidup harus dinyatakan untuk jenis terkait
Out
. JikaDeserializeBody
tidak lebih dari generik makaMyEndpoint
harus:Dan untuk mengimplementasikan persyaratan seperti itu mari kita beralih ke jenis hantu yang membutuhkan seumur hidup
'a
.Menyatukan semua bagian:
sumber
MyEndpointBody
tidak dapat meminjam dariraw_body
dalam hal itu, karena'a
hidupraw_body
anonim hidup lebih lama. Inti dari HRTB adalah untuk memberikanraw_body
masa'a
hidup.Vec<u8>
perlu dialokasikan di suatu tempat: pindah ke alokasi ke bawahdeserialize
.Saya pikir masalahnya adalah bahwa Anda meminta penangan Anda untuk dapat menangani semua kemungkinan kehidupan dengan kendala HK - yang tidak dapat dibuktikan oleh kompiler diverifikasi, oleh karena itu tidak dapat membuat kesetaraan
MyEndpointBody <=> MyEndpoint::Out
.Jika sebaliknya, Anda membuat parameter penangan Anda untuk mengambil seumur hidup tunggal , tampaknya mengkompilasi seperti yang diperlukan ( tautan taman bermain ):
sumber
for<'a> Fn(&'a [u8]) -> &'a [u8]
baik-baik saja, dan kompiler akan menerimanya. Hanya ketika tipe terkait dikembalikan yang menyebabkan masalah.FnHandler
mengambil fungsi yang, untuk setiap kemungkinan kehidupan , mengembalikan sesuatu. Ini terjadi dalam kasus Anda bahwa untuk seumur hidup'a
, itu akan selalu sama (aVec<u8>
), tetapi jika Anda tidak tahu itu, output itu mungkin tergantung pada'a
parameter seumur hidup fungsi. Meminta fungsi itu untuk mengembalikan tipe (mungkin bergantung pada seumur hidup) untuk semua masa hidup di alam semesta adalah mungkin yang membingungkan kompiler: Anda tidak dapat memverifikasi kendala ini tanpa 'melanggar lokalitas' dan mengetahui bahwa kendala Anda sebenarnya tidak bergantung seumur hidup.'static
bagaimana Anda menerapkan barang-barang untuk masa hidup yang berbeda?