从一个包装函数中借用结构体内部数据

3
我一直在努力使一个库(rust-websocket)使用更多的借用和较少的拥有数据。这涉及将一个Cow添加到一个结构体中,这又涉及向该结构体添加生命周期,从而使得整个库需要生命周期。
简而言之,我还剩下一个问题,已经尝试了数天仍未解决。可以用以下代码概括:
{   // 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);
}

在上面的示例中,client.send 是对 sender.send_message 的包装器,两者具有相同的定义。虽然在 client.send 的情况下,被发送的消息必须比客户端的生命周期更长。在 sender.send_message 的情况下,消息只需要在函数调用的生命周期内存在。
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);
    }
}

我将整个 Rust Playground上的代码放了进去。
我希望我能更好地描述我的问题,以便更好地搜索它,因此一旦我知道发生了什么,我会更改我的标题。

你说得没错,但那不是我想要的行为,我应该表述得更清楚。由于您指出的生命周期约束,所有消息都必须在客户端创建之前提前制作!我希望这些生命周期(客户端和消息)是分开的。 - Michael Eden
1
有点跑题,但我强烈建议您遵循 Rust 标准格式的 where 子句。类似这样。我在扫描您的代码时遇到了很多问题,无法看出泛型是如何使用的。我的个人偏好是始终使用 where 子句,而不是像 <A,B:Foo<A>> 这样内联它。 - Shepmaster
我也更喜欢使用 where(除了生命周期)。很高兴知道有一个标准的风格,我会很乐意遵循它。 - Michael Eden
@Shepmaster 更改了样式。 - Michael Eden
1个回答

3
PhantomData<P>更改为PhantomData<fn(P)>。您的类型不存储P,而是对P进行操作。
问题在于您声称要“存储”P,这意味着类型P必须比类型Client<S,P>存在更长时间。对于P(数据包类型)比Client类型存在更长时间,因为您正在从消息中借用数据包,则message本身必须比client存在更长时间。但是,在这种情况下,由于client首先在堆栈上分配(并且堆栈按相反顺序关闭),因此clientmessage存在更长时间。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接