English 中文(简体)
我怎么能用Arc和BufReader书写一个自我参照的Rust 结构?
原标题:How can I write a self-referential Rust struct with Arc and BufReader?

I m试图为服务器撰写以下代码:

use std::io::{BufReader, BufWriter};
use std::net::TcpStream;

struct User< a> {
    stream: Arc<TcpStream>,
    reader: BufReader<& a TcpStream>,
    writer: BufWriter<& a TcpStream>,
}

fn accept_socket(users: &mut Vec<User>, stream: Arc<TcpStream>) {
    let stream_clone = stream.clone();
    let user = User {
        stream: stream_clone,
        reader: BufReader::new(stream_clone.as_ref()),
        writer: BufWriter::new(stream_clone.as_ref()),
    };
    
    users.push(user);
}

溪流落在Arc的后面,因为它是穿透的。 The BufReader and BufWriter point to theuser s own Arc, but the codificationer complaints that the reference stream_clone.as_ref() 生活时间不够长,尽管显然确实如此(在使用者活着时,西班牙文本的斜线下降)。 我如何让汇编者接受这一守则?

问题回答

自我参照结构是一无二的。 如果拆除了这些建筑,则最根本无法更新参考资料中的地址,因为移动总是简单的比照复印件。 与C++及其搬迁建筑商不同,没有任何办法附加行动。

What you can do instead is store Arcs inside the reader and writer so they share ownership of the TcpStream.

struct User {
    stream: Arc<TcpStream>,
    reader: BufReader<IoArc<TcpStream>>,
    writer: BufWriter<IoArc<TcpStream>>,
}

The tricky part is that Arc doesn t implement Read and Write. You ll need a newtype that does (IoArc, above). Yoshua Wuyts wrote about this problem:

其中一种模式也许不太为人所知,但却是<代码>std的运行:impl Read/Write for &Type所不可或缺的。 这意味着,如果你提到国际交易日志类型,例如<代码>File 或TcpStream,那么由于某些内部交错,你仍然能够打电话<代码>Read和Write

这还意味着,如果你想分享<代码>std:fs:File,在多个校对之间,你不需要使用昂贵的<代码>Arc<Mutex<File>>,因为Arc<File>就足够了。

您可能期望,如果我们在一份<编码>Arc中填入一个国际交易日志的类别T,它将实施Clone + Read +核销<>。 但在现实中,它只是实施<条码>Clone + Deref<T>。 然而,这里有一条越轨迹:我们可以在<条码>Arc<T>上设定一个包装类型,用以实施<条码>Read +核销<>/条码>,在内部适用<条码>。

他的解决办法是:

/// A variant of `Arc` that delegates IO traits if available on `&T`.
#[derive(Debug)]
pub struct IoArc<T>(Arc<T>);

impl<T> IoArc<T> {
    /// Create a new instance of IoArc.
    pub fn new(data: T) -> Self {
        Self(Arc::new(data))
    }
}

impl<T> Read for IoArc<T>
where
    for< a> & a T: Read,
{
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        (&mut &*self.0).read(buf)
    }
}

impl<T> Write for IoArc<T>
where
    for< a> & a T: Write,
{
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        (&mut &*self.0).write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        (&mut &*self.0).flush()
    }
}

< 麻省许可证

rel=“nofollow noreferer”>io_arc crate,尽管如果你不想在依赖中拖拉,实施生态环境是不够的。

简单回答: 你们能够这样做。

在Rust,每一种类型的均隐含在肉类中。 因此,如果您的类型储存提及自己的话,那么一旦行动发生,就会打破僵局。

更复杂的答案: 除非您使用<代码>Pin,unsafe和原材料。

但我很相信,在一切情况下使用<条码>Arc是走路。

Read或

您仅能围绕<条码>Arc<TcpStream>撰写一份内容很微薄的包装材料,用于<条码>Read和<条码>。 这应当非常容易。

Edit: Take a look at @JohnKugelman s anwser for such a wrapper.

你的代码没有汇编,因为你使用了汇编者认为在运作期间是好的参考资料——即使持有该软件的<代码>Arc可能寿命更长。

But what would happen in the following scenario:

  • a User holds the last Arc to TcpStream
  • User is dropped, but the stream field is dropped first ?

在退约时,你应当谨慎行事——重新宣布声明令涉及:田地按宣布的顺序放弃。

That being said, there is indeed a recommended way to achieve self references -- it is documented in the Pin module:

https://doc.rust-lang.org/std/pin/index.html#example- Self-referential-struct

但你在此不需要,因为你不使用你自己的结构,而“搭桥”是阿尔塞在你的法典中发挥的作用。

即便如此,如果你希望通过<代码>unsafe的路线,那么你需要告诉汇编者,不要进行某些检查,因为你“知道你正在做什么”。

这里不安全的情况几乎没有任何好处(见John Kugelman关于安全版本的回答),但是,这里的任何好处都是,因为它避免了制造和安放;销毁了每条连接的2个阿尔塞,这可能会使你节省几十个或几秒。

如果没有其他文件,本文是一份比照你原来的法典的版本,汇编如下:

(但请注意,编辑人员不再证明该守则正确)

use std::io::{BufReader, BufWriter};
use std::net::TcpStream;
use std::sync::Arc;

struct User {
    reader: BufReader<& static TcpStream>,
    writer: BufWriter<& static TcpStream>,
    // notice the field order is important here:
    // `stream` must be dropped after `reader` and `writer` or else they
    // would hold invalid references. Dropping is done in the same order as declaration.
    stream: Arc<TcpStream>,
}

fn accept_socket(users: &mut Vec<User>, stream: Arc<TcpStream>) {
    let stream_ref = unsafe { &*Arc::as_ptr(&stream) };
    let user = User {
        reader: BufReader::new(stream_ref),
        writer: BufWriter::new(stream_ref),
        stream,
    };

    users.push(user);
}

fn main() {
    let stream = Arc::new(TcpStream::connect("google.com:80").unwrap());
    let mut users = Vec::with_capacity(16);  // giving a big enough capacity may squeeze a little bit more performance -- avoids reallocations
    accept_socket(&mut users, stream);
}




相关问题
Recursive calls to database in perl

I know there s an easy way of doing this, but my recursion abilities are out of practice. Given a database table that has three fields: id label child_id I should be able to put together a ...

rails: self-referential association

My needs are very simple: I have a Tip table to receive comments and have comments to receive comments, too. To retrieve each comment that is stored in the same table (comments), I created another ...

Bidirectional self referential associations

Taking Ryan Bates asciicast as an example: http://asciicasts.com/episodes/163-self-referential-association He ends with two associations of User :friends :inverse_friends Given that a user would ...

class __init__ (not instance __init__)

Here s a very simple example of what I m trying to get around: class Test(object): some_dict = {Test: True} The problem is that I cannot refer to Test while it s still being defined Normally, I ...

How to refer to object in JavaScript event handler?

Note: This question uses jQuery but the question has nothing to do with jQuery! Okay so I have this object: var box = new BigBox(); This object has a method named Serialize(): box.AddToPage(); ...

Self-reference entity in Hibernate

I have an Action entity, that can have other Action objects as child in a bidirectional one-to-many relationship. The problem is that Hibernate outputs the following exception: "Repeated column in ...

Entity Framework Self Referencing Hierarchical Many To Many

OK this problem has it all. Conceptually I have a Resource entity which can have many Child Resources and many Parent Resources. The Resource table has two fields, ID and Name with ID being the ...

热门标签