繁体   English   中英

结构内的 Rust 结构:借用、生命周期、泛型类型和更多完全混淆

[英]Rust struct within struct: borrowing, lifetime, generic types and more total confusion

我正在尝试修改迫使我学习 rust 的现有应用程序,这让我很难过(重新制定......)

我想要一个包含两个字段的结构:

pub struct Something<'a> {
    pkt_wtr: PacketWriter<&'a mut Vec<u8>>,
    buf: Vec<u8>,
}

其中“buf”将用作 io 供 PacketWriter 写入其结果。 所以 PacketWriter 类似于

use std::io::{self};

pub struct PacketWriter<T :io::Write> {
    wtr :T,
}

impl <T :io::Write> PacketWriter<T> {
    pub fn new(wtr :T) -> Self {
        return PacketWriter {
            wtr,
        };
    }
    pub fn into_inner(self) -> T {
        self.wtr
    }
    pub fn write(&mut self) {
        self.wtr.write_all(&[10,11,12]).unwrap();
        println!("wrote packet");
    }
}

然后在 'Something' 中,我想以这种方式使用 PacketWriter:让它在 'buf' 中写入它需要的内容,然后将其逐个排出。

impl Something<'_> {
    pub fn process(&mut self) {
        self.pkt_wtr.write();
        let c = self.buf.drain(0..1);
    }
}

似乎不可能为“某事”创建一个可行的构造函数

impl Something<'_> {
    pub fn new() -> Self {
        let mut buf = Vec::new();
        let pkt_wtr = PacketWriter::new(&mut buf);        
        return Something {
            pkt_wtr: pkt_wtr,
            buf: buf,
        };
    }
}

然而,似乎不可行的是,在从“buf”借来的引用上构建 PacketWriter,而“buf”也存储在“Something”object 中。

我可以将 'buf' 完全赋予 'PacketWriter' (下面的示例),但之后我无法访问 'buf' 的内容。 我知道它在下面的示例中有效,但这是因为在将它提供给“PacketWriter”(通过“wtr”)之后我可以访问“buf”。实际上,“PacketWriter”具有该字段(wtr ) 私有,此外它是我无法修改的代码,例如,获取“wtr”的 getter

谢谢

我写了一个小的工作程序来描述意图和问题,有两个选项

use std::io::{self};

pub struct PacketWriter<T :io::Write> {
    wtr :T,
}

impl <T :io::Write> PacketWriter<T> {
    pub fn new(wtr :T) -> Self {
        return PacketWriter {
            wtr,
        };
    }
    pub fn into_inner(self) -> T {
        self.wtr
    }
    pub fn write(&mut self) {
        self.wtr.write_all(&[10,11,12]).unwrap();
        println!("wrote packet");
    }
}

/*
// that does not work of course because buf is local but this is not the issue
pub struct Something<'a> {
    pkt_wtr: PacketWriter<&'a mut Vec<u8>>,
    buf: Vec<u8>,
}

impl Something<'_> {
    pub fn new() -> Self {
        let mut buf = Vec::new();
        let pkt_wtr = PacketWriter::new(&mut buf);
        //let mut pkt_wtr = PacketWriter::new(buf);     
        return Something {
            pkt_wtr,
            buf,
        };
    }
    pub fn process(&mut self) {
        self.pkt_wtr.write();
        println!("process {:?}", self.buf);
    }
}
*/

pub struct Something {
    pkt_wtr: PacketWriter<Vec<u8>>,
}

impl Something {
    pub fn new() -> Self {
        let pkt_wtr = PacketWriter::new(Vec::new());
        return Something {
            pkt_wtr,
        };
    }
    pub fn process(&mut self) {
        self.pkt_wtr.write();
        let file = &mut self.pkt_wtr.wtr;
        println!("processing Something {:?}", file);            
        let c = file.drain(0..1);
        println!("Drained {:?}", c);
    }
}

fn main() -> std::io::Result<()> {
    let mut file = Vec::new();
    let mut wtr = PacketWriter::new(&mut file);

    wtr.write();

    println!("Got data {:?}", file);
    {
        let c = file.drain(0..2);
        println!("Drained {:?}", c);     
    } 
    println!("Remains {:?}", file);
    
    let mut data = Something::new();
    data.process();

    Ok(())
}

鉴于代码似乎可以编译,目前尚不完全清楚问题是什么,但我可以在一个部分进行尝试:为什么不能在process self.wtr内的 self.wtr 上使用into_inner()

into_inner拥有被传递到其self参数的PacketWriter的所有权。 (您可以这样说,因为参数拼写为self ,而不是&self&mut self 。)取得所有权意味着它已被消耗:调用者不能再使用它,而被调用者负责删除它(阅读:运行析构函数) . 在获得PacketWriter的所有权后, into_inner function 只返回wtr字段并丢弃(在其上运行析构函数)rest。 但是, Something结构在哪里? 它有一个需要包含PacketWriter的字段,而你只是将它的PacketWriter拿走并销毁它,function 结束, PacketWriter字段中保存的值是未知的:它不可能是从一开始就存在的东西,因为它被into_inner并销毁。 但也不能是别的。

Rust 通常禁止结构具有未初始化或未定义的字段。 您需要始终定义该字段。

这是工作示例:

pub fn process(&mut self) {
    self.pkt_wtr.write();

    // There's a valid PacketWriter in pkt_wtr

    let raw_wtr: Vec<u8> = self.pkt_wtr.into_inner();

    // The PacketWriter in pkt_wtr was consumed by into_inner!
    // We have a raw_wtr of type Vec<u8>, but that's not the right type for pkt_wtr

    // We could try to call this function here, but what would it do?
    self.pkt_wtr.write();

    println!("processing Something");
}

(注意:上面的例子逻辑有点糊涂。形式上,因为你不拥有self ,所以你不能做任何会拥有它任何部分的所有权的事情,即使你完成后把所有东西都整齐地放回去。 )

您有几个选项可以解决此问题,但有一个主要警告:使用您描述的公共接口,无法访问PacketWriter::wtr字段并将其放回同一个PacketWriter 您必须提取PacketWriter::wtr字段并将其放入新的PacketWriter中。

这是您可以做到的一种方法。 请记住,目标是始终定义self.packet_wtr ,因此我们将使用名为mem::replace的 function 将虚拟PacketWriter放入self.pkt_wtr 这确保了self.pkt_wtr总是有一些东西在里面。

pub fn process(&mut self) {
    self.pkt_wtr.write();

    // Create a new dummy PacketWriter and swap it with self.pkt_wtr
    // Returns an owned version of pkt_wtr that we're free to consume
    let pkt_wtr_owned = std::mem::replace(&mut self.pkt_wtr, PacketWriter::new(Vec::new()));

    // Consume pkt_wtr_owned, returning its wtr field
    let raw_wtr = pkt_wtr_owned.into_inner();

    // Do anything you want with raw_wtr here -- you own it.
    println!("The vec is: {:?}", &raw_wtr);

    // Create a new PacketWriter with the old PacketWriter's buffer.
    // The dummy PacketWriter is dropped here.
    self.pkt_wtr = PacketWriter::new(raw_wtr);

    println!("processing Something");
}

Rust游乐场

这个解决方案绝对是一个 hack,并且它可能是一个可以改进借用检查器的地方,以实现暂时未定义的字段是可以的,只要在再次分配之前没有访问它。 (尽管我可能错过了一个边缘情况;这些东西通常很难推理。)此外,这是可以通过以后的编译器通过死存储消除来优化掉的东西。

如果在分析时证明这是一个热点,则存在unsafe的技术会使该字段在该期间无效,但这可能需要一个新问题。

但是,我的建议是找到一种方法,将“逃生舱” function 添加到PacketWriter中,让您完全按照自己的意愿去做:获得对内部wtr的可变引用,而无需获取PacketWriter的所有权。

impl<T: io::Write> PacketWriter<T> {
    pub fn inner_mut(&mut self) -> &mut T {
        &mut self.wtr
    }
}

为了澄清,我找到了使用 Rc+RefCell 或 Arc+Mutex 的解决方案。 我将缓冲区封装在 Rc/RefCell 中并添加了一个 Write

pub struct WrappedWriter {
    data :Arc<Mutex<Vec<u8>>>,
}

impl WrappedWriter {
    pub fn new(data : Arc<Mutex<Vec<u8>>>) -> Self {
        return WrappedWriter {
            data,
        };
    }
}    

impl Write for WrappedWriter {
    fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
        let mut data = self.data.lock().unwrap();
        data.write(buf)
    }
    fn flush(&mut self) -> Result<(), Error> {
        Ok(())
    }
}

pub struct Something {
    wtr: PacketWriter<WrappedWriter>,
    data : Arc<Mutex<Vec<u8>>>,
}

impl Something {
    pub fn new() -> Result<Self, Error> {
        let data :Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(Vec::new()));
        let wtr = PacketWriter::new(WrappedWriter::new(Arc::clone(&data)));

        return Ok(PassthroughDecoder {
            wtr,
            data,
        });
    }

    pub fn process(&mut self) {
         let mut data = self.data.lock().unwrap();
         data.clear();
    }
}

如果您没有线程安全问题,您可以用 Rc 替换 Arc,用 RefCell 替换 Mutex,在这种情况下,引用访问变为

let data = self.data.borrow_mut();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM