Làm thế nào để tôi trả về một loại liên quan từ một đặc điểm ràng buộc xếp hạng cao hơn?


11

Tôi có một đặc điểm có chức năng khử lưu huỳnh một loại liên quan. Tuy nhiên, loại liên quan đó cần phải có thời gian tồn tại mà người gọi quyết định, vì vậy tôi có một đặc điểm riêng mà tôi sử dụng một đặc điểm được xếp hạng cao hơn bị ràng buộc, để nó có thể được giải trừ trong suốt cuộc đời.

Tôi cần sử dụng một bao đóng trả về loại liên quan này.

Tôi có đoạn mã sau để làm điều đó:

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

Tôi nghĩ rằng nó sẽ hoạt động, nhưng khi tôi kiểm tra nó, tôi gặp một lỗi loại:

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

Thật khó hiểu vì đây MyEndpoint::Outlà một MyEndpointBodythứ mà tôi đang trở về sau khi đóng cửa, nhưng Rust không nghĩ chúng cùng loại. Tôi đoán đó là vì Rust chọn kiếp vô danh không tương thích cho các MyEndpointBodyloại, nhưng tôi không biết làm thế nào để khắc phục điều đó.

Làm cách nào để mã này hoạt động để tôi có thể sử dụng bao đóng với loại liên kết HRTB?

Câu trả lời:


4

Việc đóng gói bọc kiểu trả về trong một kiểu mới sẽ khắc phục vấn đề:

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

Tôi muốn nói rằng đây là một lỗi của trình biên dịch Rust, vì cho rằng newtype sẽ giống như kiểu liên quan. Dường như cũng có một số ICE liên quan đến việc sử dụng các loại liên quan đến HRTB: https://github.com/rust-lang/rust/issues/62529


0

Bạn có thể vui lòng kiểm tra rằng một

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

Đây có thể không phải là một giải pháp tổng quát, vì Fntham số cần phải có thời gian tồn tại tùy ý. Nhưng ở đây, cuộc sống này trở nên phụ thuộc và nó khiến việc sử dụng này trở nên không thể, vui lòng kiểm tra: play.rust-lang.org/iêu
Ömer Erden

Thật không may trong khi điều này hoạt động với ví dụ đơn giản, nó không hoạt động với mã tôi có cho dự án của tôi. Tôi sẽ cập nhật ví dụ của mình để minh họa rõ hơn những gì tôi đang làm.
Đại tá Ba mươi Hai

0

Xác định DeserializeBodylà:

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

Outlà một tuyên bố của một loại chung chung. Đừng tuyên bố ràng buộc suốt đời ở đây, nó sẽ được rõ ràng tại trang web định nghĩa.

Tại thời điểm này, không còn cần thiết nữa Giới hạn Đặc điểm Cấp cao cho Endpoint:

trait Endpoint: DeserializeBody {}

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

Tại trang định nghĩa, yêu cầu trọn đời phải được thể hiện cho loại liên quan Out. Nếu DeserializeBodykhông phải là chung chung thì MyEndpointphải là:

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

    ...

Và để thực hiện yêu cầu như vậy cho phép sử dụng một loại bóng ma đòi hỏi cả đời 'a.

Đặt tất cả các mảnh lại với nhau:

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

Không. MyEndpointBodykhông thể vay mượn từ raw_bodytrong trường hợp đó, vì 'aoutlives raw_bodyđời vô danh 's. Toàn bộ quan điểm của HRTB là mang lại raw_bodysự 'atrọn đời.
Đại tá Ba mươi Hai

Ồ, tôi hiểu rồi. Với HRTB, bạn đang cố gắng khử lưu lượng hóa trong suốt cuộc đời và sau đó mượn từ dữ liệu đầu vào. Một phần có vẻ là một hạn chế của trình biên dịch, có vẻ như giải pháp của bạn là serde :: DeserializeOwned và serde impl không thể mượn bất kỳ dữ liệu nào từ trình giải nén .
attdona

Nên giải quyết này quả với bạn không? Vec<u8>cần được phân bổ ở đâu đó: chuyển xuống phân bổ vào deserialize.
attdona

Vâng, tôi chỉ có thể từ bỏ và loại bỏ cuộc sống, nhưng sau đó tôi không thể khử lưu huỳnh bằng 0 và nó đánh bại điểm của câu hỏi.
Đại tá Ba mươi Hai

0

Tôi nghĩ vấn đề là bạn yêu cầu người xử lý của bạn có thể xử lý tất cả các vòng đời có thể với ràng buộc HK đó - điều mà trình biên dịch không thể chứng minh được xác minh, do đó không thể thực hiện tương đương MyEndpointBody <=> MyEndpoint::Out.

Nếu thay vào đó, bạn parameterise xử lý của bạn để có một đơn suốt đời, nó xuất hiện để biên dịch theo yêu cầu ( liên kết sân chơi ):

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

Tôi không hiểu đoạn đầu tiên của bạn. Bạn có thể làm, ví dụ, for<'a> Fn(&'a [u8]) -> &'a [u8]chỉ cần tốt, và trình biên dịch sẽ chấp nhận nó. Chỉ khi loại liên quan được trả về gây ra sự cố.
Đại tá Ba mươi Hai

Tôi có nghĩa là bạn FnHandlercó một chức năng, mà trong mỗi đời có thể , trả lại một cái gì đó. Trong trường hợp của bạn, trong mọi trường hợp 'a, nó sẽ luôn giống nhau (a Vec<u8>), nhưng nếu bạn không biết điều đó, thì đầu ra đó có thể phụ thuộc vào 'atham số trọn đời của hàm. Yêu cầu hàm đó trả về loại (có thể phụ thuộc trọn đời) cho tất cả các thời gian sống trong vũ trụ có thể là điều khiến trình biên dịch bối rối: bạn không thể xác minh ràng buộc này mà không 'phá vỡ địa phương' và biết rằng ràng buộc của bạn thực sự không phụ thuộc trọn đời.
val

Đó không phải là trường hợp, xem như trình bao bọc newtype trong câu trả lời của tôi chỉ hoạt động tốt trong khi sử dụng loại liên quan. Tôi không nghĩ rằng bạn thậm chí có thể có các loại liên quan khác nhau cho các vòng đời khác nhau; vòng đời duy nhất được đặt tên có sẵn ở phạm vi toàn cầu nơi bạn phải đặt impls là 'staticvậy bạn sẽ triển khai công cụ như thế nào cho các vòng đời khác nhau?
Đại tá Ba mươi Hai
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.