Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Borrow inner data from struct in a wrapper function

Tags:

rust

I've been working on making a library (rust-websocket) use more borrows and less owned data. This involved adding a Cow to a struct, which involved adding a lifetime to that struct, which made the entire library need lifetimes.

In short I have one problem left, and I've been trying to solve it for days. It can be summarized with this code:

{   // This works great!
    let mut sender = Wire;
    let message = Text("Hello World!".to_string());
    sender.send_message(&message);
}
{   // This DOES NOT COMPILE!
    let mut client = Client {
        sender: Wire,
        packet: PhantomData,
    };
    let message = Text("Hello World!".to_string());
    client.send(&message);
}

In the above example client.send is a wrapper around sender.send_message, both have the same definition. Although in the case of client.send the message being send must live longer than the client. In the sender.send_message case the message only has to live for the lifetime of the function call.

use std::borrow::Cow;
use std::iter::{Take, Repeat, repeat};
use std::marker::PhantomData;

trait Sender<P> {
    fn send_packet(&mut self, packet: &P) -> Result<(), ()>;

    fn send_message<'m, M>(&mut self, message: &'m M) -> Result<(), ()>
        where M: Message<'m, P>,
              P: 'm
    {
        for ref packet in message.iter() {
            try!( self.send_packet(packet) );
        }
        Ok(())
    }
}

trait Message<'p, P>
    where P: 'p
{
    type PacketIterator: Iterator<Item = P>;
    fn iter(&'p self) -> Self::PacketIterator;
}

#[derive(Clone, Debug)]
struct Packet<'d> {
    data: Cow<'d, [u8]>,
}

struct Text(String);

impl<'p> Message<'p, Packet<'p>> for Text {
    type PacketIterator = Take<Repeat<Packet<'p>>>;

    fn iter(&'p self) -> Take<Repeat<Packet<'p>>> {
        repeat(Packet {
            data: Cow::Borrowed(self.0.as_bytes()),
        }).take(1)
    }
}

struct Wire;

impl<'s> Sender<Packet<'s>> for Wire {
    fn send_packet<'p>(&mut self, packet: &Packet<'p>) -> Result<(), ()> {
        println!("Sent {:?}", packet);
        Ok(())
    }
}

struct Client<P, S> {
    sender: S,
    packet: PhantomData<P>
}

impl<P, S> Client<P, S>
    where S: Sender<P>
{
    fn send<'m, M>(&mut self, message: &'m M) -> Result<(), ()>
        where M: Message<'m, P>,
              P: 'm
    {
        self.sender.send_message(message)
    }
}

fn main() {
    {   // This works great!
        let mut sender = Wire;
        let message = Text("Hello World!".to_string());
        sender.send_message(&message);
    }
    {   // This DOES NOT COMPILE!
        let mut client = Client {
            sender: Wire,
            packet: PhantomData,
        };
        let message = Text("Hello World!".to_string());
        client.send(&message);
    }
}

I put the entire code on the Rust Playground

I wish I could better describe my problem so it can be better searched for, so I will change my title once I know what's going on.

like image 458
Michael Eden Avatar asked Dec 28 '25 18:12

Michael Eden


1 Answers

Change PhantomData<P> to PhantomData<fn(P)>. Your type doesn't store P, it operates on P.

The problem here is that you're claiming to store a P which means that the type P must outlive the type Client<S, P>. For P (the packet type) to outlive the Client type, message itself must outlive client because you're borrowing packets from messages. However, in this case, client outlives message because client is allocated first on the stack (and stacks are torn down in reverse order).

like image 173
Steven Avatar answered Dec 30 '25 16:12

Steven



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!