ฉันจะส่งคืนประเภทที่เชื่อมโยงจากคุณลักษณะที่ถูกจัดอันดับที่สูงกว่าได้อย่างไร


11

ฉันมีลักษณะที่มีฟังก์ชั่นสำหรับ deserializing ประเภทที่เกี่ยวข้อง อย่างไรก็ตามประเภทที่เกี่ยวข้องนั้นจำเป็นต้องมีอายุการใช้งานที่ผู้โทรตัดสินใจดังนั้นฉันจึงมีคุณลักษณะที่แยกต่างหากซึ่งฉันใช้คุณลักษณะที่มีอันดับสูงกว่าซึ่งผูกไว้กับ

ฉันต้องใช้การปิดที่ส่งคืนประเภทที่เชื่อมโยงนี้

ฉันมีรหัสต่อไปนี้เพื่อทำ:

#![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มันคือ a MyEndpointBodyซึ่งฉันกลับมาจากการปิด แต่ Rust ไม่คิดว่ามันจะเป็นแบบเดียวกัน ฉันเดาว่าเพราะ Rust เลือกอายุการใช้งานที่ไม่ระบุตัวตนของMyEndpointBodyประเภท แต่ฉันไม่รู้ว่าจะแก้ไขได้อย่างไร

ฉันจะทำให้โค้ดนี้ทำงานได้อย่างไรเพื่อที่ฉันจะได้ใช้การปิดกับประเภทที่เกี่ยวข้องกับ HRTB?

คำตอบ:


4

การปิดส่วนที่ห่อหุ้มชนิดส่งคืนใน newtype จะช่วยแก้ไขปัญหา:

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

ฉันอยากจะบอกว่านี่เป็นข้อผิดพลาดของคอมไพเลอร์ Rust โดยพิจารณาว่ารูปแบบใหม่ควรจะเหมือนกันกับประเภทที่เกี่ยวข้อง นอกจากนี้ยังมี ICE บางส่วนที่เกี่ยวข้องกับการใช้ประเภทที่เกี่ยวข้องกับ HRTB: https://github.com/rust-lang/rust/issues/62529


0

คุณช่วยกรุณาตรวจสอบที่หนึ่ง

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" });
}

สิ่งนี้อาจไม่ใช่โซลูชันทั่วไปเนื่องจากFnพารามิเตอร์ของต้องมีอายุการใช้งานโดยพลการ แต่ที่นี่อายุการใช้งานจะขึ้นอยู่กับและทำให้การใช้งานนี้เป็นไปไม่ได้โปรดตรวจสอบ: play.rust-lang.org/…
Ömer Erden

น่าเสียดายที่ในขณะนี้ใช้งานได้กับตัวอย่างง่ายๆมันไม่ทำงานกับรหัสที่ฉันมีสำหรับโครงการของฉัน ฉันจะอัปเดตตัวอย่างของฉันให้ดีขึ้นเพื่ออธิบายสิ่งที่ฉันทำ
พันเอกสามสิบสอง

0

กำหนดDeserializeBodyเป็น:

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

Outเป็นการประกาศประเภททั่วไป อย่าประกาศอายุการใช้งานที่ถูกผูกไว้ที่นี่มันจะชัดเจนที่ไซต์คำจำกัดความ

เมื่อมาถึงจุดนี้มันก็ไม่จำเป็นที่จะต้องมีระดับสูงกว่าEndpoint:

trait Endpoint: DeserializeBody {}

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

Outที่เว็บไซต์นิยามความต้องการอายุการใช้งานจะต้องมีการแสดงสำหรับชนิดที่เกี่ยวข้อง ถ้าDeserializeBodyไม่ใช่แบบทั่วไปก็MyEndpointจะต้อง:

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

    ...

'aและเพื่อให้การดำเนินการดังกล่าวจะช่วยให้ความต้องการของรีสอร์ทไปยังประเภทผีที่ต้องใช้ตลอดชีวิต

วางชิ้นส่วนทั้งหมดเข้าด้วยกัน:

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" });
}

Nope MyEndpointBodyไม่สามารถกู้ยืมเงินจากraw_bodyในกรณีที่ว่าเพราะ'aoutlives raw_bodyอายุการใช้งานที่ไม่ระบุชื่อ 's จุดทั้งหมดของ HRTB คือการให้อายุการใช้งาน raw_body'a
พันเอกสามสิบสอง

อ้อเข้าใจแล้ว. ด้วย HRTB คุณพยายามที่จะปลดระวางตลอดอายุการใช้งานและหลังจากนั้นก็ยืมจากข้อมูลอินพุต ส่วนที่ดูเหมือนว่าข้อ จำกัด ของคอมไพเลอร์สิ่งที่ดูเหมือนโซลูชันของคุณคือserde :: DeserializeOwnedและ serde impl ไม่สามารถกู้ข้อมูลใด ๆ จาก deserializer
attdona

วิธีแก้ปัญหานี้ควรเหมาะกับคุณหรือไม่? Vec<u8>ความต้องการที่จะได้รับการจัดสรรที่ใดที่หนึ่ง: deserializeย้ายลงการจัดสรรลงใน
attdona

ใช่ฉันสามารถทิ้งและกำจัดชีวิตได้ แต่จากนั้นฉันก็ไม่สามารถกำจัดดีซีเรียลไลเซชันแบบไม่มีศูนย์ได้และมันก็เอาชนะจุดคำถามได้
พันเอกสามสิบสอง

0

ฉันคิดว่าปัญหาคือคุณขอให้ผู้จัดการของคุณสามารถจัดการกับอายุการใช้งานที่เป็นไปได้ทั้งหมดด้วยข้อ จำกัด ของ HK - ซึ่งคอมไพเลอร์ไม่สามารถพิสูจน์ได้ว่ามีการตรวจสอบแล้วดังนั้นจึงไม่สามารถทำการเทียบเท่าMyEndpointBody <=> MyEndpoint::Outได้

หากคุณตั้งค่าตัวจัดการของคุณให้ใช้เวลาหนึ่งปี แต่ดูเหมือนว่าจะรวบรวมตามต้องการ ( ลิงก์สนามเด็กเล่น ):

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

ฉันไม่เข้าใจย่อหน้าแรกของคุณ ตัวอย่างเช่นคุณสามารถทำได้for<'a> Fn(&'a [u8]) -> &'a [u8]ดีและคอมไพเลอร์จะยอมรับมัน เป็นเพียงเมื่อชนิดที่เกี่ยวข้องถูกส่งคืนซึ่งทำให้เกิดปัญหา
พันเอกสามสิบสอง

ฉันหมายถึงการที่คุณFnHandlerใช้ฟังก์ชั่นซึ่งส่งคืนบางสิ่งบางอย่างไปตลอดชีวิต มันเกิดขึ้นในกรณีของคุณว่าสำหรับอายุการใช้งานใด ๆ'aมันจะเป็นแบบเดียวกันเสมอ (a Vec<u8>) แต่ถ้าคุณไม่ทราบว่าผลลัพธ์นั้นอาจขึ้นอยู่กับอายุการใช้งานของ'aพารามิเตอร์ของฟังก์ชัน ร้องขอฟังก์ชั่นที่จะกลับมาที่ (อาจจะขึ้นอยู่กับอายุการใช้งาน) ประเภทสำหรับชีวิตทั้งหมดในจักรวาลอาจจะเป็นสิ่งที่สร้างความสับสนให้คอมไพเลอร์: คุณไม่สามารถยืนยันข้อ จำกัด นี้โดย 'หมดท้องที่' และรู้ว่าข้อ จำกัด ของคุณเป็นจริงไม่ได้อายุการใช้งานขึ้นอยู่กับ
วาล

ไม่เป็นเช่นนั้นการเห็นว่า wrapper ชนิดใหม่ในคำตอบของฉันทำงานได้ดีในขณะที่ใช้ประเภทที่เกี่ยวข้อง ฉันไม่คิดว่าคุณสามารถมีประเภทที่เกี่ยวข้องที่แตกต่างกันสำหรับอายุการใช้งานที่แตกต่างกัน อายุการใช้งานที่ระบุชื่อเพียงอย่างเดียวที่มีอยู่ในขอบเขตทั่วโลกที่คุณต้องใส่ impls คือ'staticดังนั้นคุณจะนำสิ่งต่าง ๆ ไปใช้ในช่วงชีวิตที่แตกต่างกันอย่างไร
พันเอกสามสิบสอง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.