Bagaimana cara mengembalikan tipe terkait dari sifat terikat dengan peringkat lebih tinggi?

11

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::Outadalah 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 MyEndpointBodyjenis ini, tapi saya tidak tahu bagaimana cara memperbaikinya.

Bagaimana saya bisa membuat kode ini berfungsi sehingga saya bisa menggunakan penutupan dengan tipe terkait HRTB?

Kolonel Thirty Two
sumber

Jawaban:

4

Memiliki penutup bungkus tipe kembali dalam tipe baru memperbaiki masalah:

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

Saya tergoda untuk mengatakan ini adalah bug compiler Rust, mengingat bahwa tipe baru harus hampir sama dengan tipe yang terkait. Tampaknya juga ada beberapa ICE yang berkaitan dengan penggunaan tipe terkait HRTB: https://github.com/rust-lang/rust/issues/62529

Kolonel Thirty Two
sumber
0

Bisa tolong periksa bahwa salah satu

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" });
}
Maks
sumber
Ini mungkin bukan solusi umum, karena Fnparameter 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/…
Ömer Erden
Sayangnya, sementara ini bekerja dengan contoh sederhana, itu tidak bekerja dengan kode yang saya miliki untuk proyek saya. Saya akan memperbarui contoh saya untuk lebih menggambarkan apa yang saya lakukan.
Kolonel Thirty Two
0

Tentukan DeserializeBodysebagai:

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

Outadalah 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:

trait Endpoint: DeserializeBody {}

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

Di situs definisi, persyaratan seumur hidup harus dinyatakan untuk jenis terkait Out. Jika DeserializeBodytidak lebih dari generik maka MyEndpointharus:

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

    ...

Dan untuk mengimplementasikan persyaratan seperti itu mari kita beralih ke jenis hantu yang membutuhkan seumur hidup 'a.

Menyatukan semua bagian:

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" });
}
attdona
sumber
Nggak. MyEndpointBodytidak dapat meminjam dari raw_bodydalam hal itu, karena 'ahidup raw_bodyanonim hidup lebih lama. Inti dari HRTB adalah untuk memberikan raw_bodymasa 'ahidup.
Kolonel Thirty Two
Oh begitu. Dengan HRTB Anda mencoba deserialize untuk seumur hidup, dan setelah itu meminjam dari data input. Bagian yang tampaknya merupakan batasan dari kompiler, yang kelihatannya solusi Anda adalah serde :: DeserializeOwned dan serde impl tidak dapat meminjam data apa pun dari deserializer.
attdona
Haruskah solusi ini berhasil untuk Anda? Vec<u8>perlu dialokasikan di suatu tempat: pindah ke alokasi ke bawah deserialize.
attdona
Yah ya, saya bisa menyerah dan menghapus masa hidup, tapi kemudian saya tidak bisa memiliki deserialisasi nol-salinan dan itu mengalahkan titik pertanyaan.
Kolonel Thirty Two
0

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 ):

#![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(&[]);
}
val
sumber
Saya tidak mengerti paragraf pertama Anda. Anda dapat melakukannya, misalnya, for<'a> Fn(&'a [u8]) -> &'a [u8]baik-baik saja, dan kompiler akan menerimanya. Hanya ketika tipe terkait dikembalikan yang menyebabkan masalah.
Kolonel Thirty Two
Saya maksudkan bahwa Anda FnHandlermengambil fungsi yang, untuk setiap kemungkinan kehidupan , mengembalikan sesuatu. Ini terjadi dalam kasus Anda bahwa untuk seumur hidup 'a, itu akan selalu sama (a Vec<u8>), tetapi jika Anda tidak tahu itu, output itu mungkin tergantung pada 'aparameter 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.
val
Bukan itu masalahnya, mengingat pembungkus tipe baru dalam jawaban saya berfungsi dengan baik saat menggunakan jenis yang terkait. Saya tidak berpikir Anda bahkan dapat memiliki tipe terkait yang berbeda untuk masa hidup yang berbeda; satu-satunya nama seumur hidup yang tersedia di lingkup global di mana Anda harus meletakkan impls adalah 'staticbagaimana Anda menerapkan barang-barang untuk masa hidup yang berbeda?
Kolonel Thirty Two