struct - Rust struct : borrowing, 生命周期内的结构,泛型类型和更多的完全困惑

标签 struct rust borrowing

我正在尝试修改一个迫使我学习 rust 的现有应用程序,这让我很难过(重新制定......)
我想要一个包含两个字段的结构:

pub struct Something<'a> {
    pkt_wtr: PacketWriter<&'a mut Vec<u8>>,
    buf: Vec<u8>,
}
其中 'buf' 将用作 PacketWriter 写入其结果的 io。所以 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”对象中。
我可以将 'buf' 完全赋予 'PacketWriter' (下面的示例),但之后我无法访问 'buf' 的内容。我知道它在下面的示例中有效,但这是因为在将它提供给“PacketWriter”(通过“wtr”)之后我可以访问“buf”。实际上,“PacketWriter”具有该字段(wtr ) 私有(private),此外它是我无法修改的代码,例如,获取“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(())
}

最佳答案

鉴于代码似乎可以编译,目前尚不完全清楚问题是什么,但我可以试一试:你为什么不能使用 into_inner()self.wtr里面process功能?into_inner拥有PacketWriter被传递到它的self范围。 (您可以这样说,因为 parameter 拼写为 self,而不是 &self&mut self。)取得所有权意味着它已被消耗:调用者不能再使用它,而被调用者负责丢弃它(阅读:运行析构函数)。获得 PacketWriter 的所有权后, into_inner函数只返回 wtr字段并丢弃(在其上运行析构函数)其余部分。但这会将 Something 留在哪里?结构?它有一个字段需要包含 PacketWriter ,而你只是拿走了它的PacketWriter离开并摧毁它!函数结束,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 ,所以你不能做任何会获得它任何部分所有权的事情,即使你把所有东西都整齐地放回原处。完毕。)
您有几个选项可以解决此问题,但有一个主要警告:使用您描述的公共(public)界面,无法访问 PacketWriter::wtr字段并将其放回相同的PacketWriter .您必须提取 PacketWriter::wtr字段并将其放入新的 PacketWriter .
这是您可以做到的一种方法。请记住,我们的目标是拥有 self.packet_wtr始终定义,所以我们将使用一个名为 mem::replace 的函数放一个假人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 Playground
这个解决方案绝对是一个 hack,它可能是一个可以改进借用检查器的地方,以实现暂时未定义的字段是可以的,只要在再次分配之前没有访问它。 (虽然我可能错过了一个极端情况;这些东西通常很难推理。)此外,这是可以通过以后的编译器通过 dead store elimination 优化掉的东西。 .
如果在分析时这是一个热点,则有 unsafe允许该字段在该期间无效的技术,但这可能需要一个新问题。
但是,我的建议是想办法将“逃生舱”功能添加到 PacketWriter。这让你可以做你想做的事:获取对内部 wtr 的可变引用未取得 PacketWriter 的所有权.
impl<T: io::Write> PacketWriter<T> {
    pub fn inner_mut(&mut self) -> &mut T {
        &mut self.wtr
    }
}

关于struct - Rust struct : borrowing, 生命周期内的结构,泛型类型和更多的完全困惑,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65526576/

相关文章:

C - 创建一个查找表,将数据返回到 union : either a bitwise struct or a short

c - 将任意大小的二维结构数组传递给 C 中的函数

c - 为什么结构的前向声明在我的代码中不起作用?什么时候可以在C中使用?

rust - 如何将可变长度数组添加到 Vec 中?

rust - 是否可以创建一个自定义派生来防止编译时类型之间的循环?

rust - 为什么这个值活得不够长?

rust - 重复更新和输出数据结构

c - C 中返回结构表的函数

rust - 使用 mailparse 的 ParsedMail::get_body 时二进制附件损坏

memory-management - Rust 结构体上 getter 方法的借用问题