您如何创建部分初始化的结构?

How are you able to create partially initialised structs?

在 Rust 中创建结构时,似乎很难在不设置所有字段的情况下创建结构。例如使用以下代码

struct Connection {
    url: String,
    stream: TcpStream
}

如果不提供 stream,则无法设置 url

// Compilation error asking for 'stream'
let m = Connection { url: "www.google.com".to_string() }; 

您如何才能创建这些可能 Option<None> 直到稍后的引用?

我发现的最好的方法是使用 Default 特性,但我宁愿不必创建 TcpStream 直到稍后的时间,而不是在初始化结构时。我可以用 Box 之类的东西来做到这一点吗?

您可以做的一件事是将 TcpStream 包裹在 Option 中,即 Option<TcpStream>。当您第一次构建结构时,它将是 None,当您初始化它时,您将其设为 self.stream = Some(<initialize tcp stream>)。无论你在哪里使用 TCPStream,你都必须检查它是否是 Some,即它是否已经被初始化。如果你能保证你的行为,那么你就可以unwrap(),但最好还是检查一下。

struct Connection {
    url: String,
    stream: Option<TcpStream>
}

impl Connection {
    pub fn new() -> Connection {
        Connection {
            url: "www.google.com".to_string(),
            stream: None,
        }
    }

    pub fn initialize_stream(&mut self) {
        self.stream = Some(TcpStream::connect("127.0.0.1:34254").unwrap());
    }

    pub fn method_that_uses_stream(&self) {
        if let Some(ref stream) = self.stream {
            // can use the stream here
        } else {
            println!("the stream hasn't been initialized yet");
        }
    }
}

如果您熟悉该语言,这与 Swift 中的操作类似。

创建 struct 实例时确实必须初始化所有字段(Rust 中没有 null),因此分配了所有内存。 通常有一个专门的方法(如 new)为应该在稍后阶段修改的字段设置默认值。

当您不知道字段的大小时(就像 Vec 那样),我会使用 Box

作为 的扩展,您可以使用 builder。构建器具有所有可选字段并生成没有 Options 的最终值:

use std::net::TcpStream;

struct ConnectionBuilder {
    url: String,
    stream: Option<TcpStream>,
}

impl ConnectionBuilder {
    fn new(url: impl Into<String>) -> Self {
        Self {
            url: url.into(),
            stream: None,
        }
    }

    fn stream(mut self, stream: TcpStream) -> Self {
        self.stream = Some(stream);
        self
    }

    fn build(self) -> Connection {
        let url = self.url;
        let stream = self
            .stream
            .expect("Perform actual error handling or default value");
        Connection { url, stream }
    }
}

struct Connection {
    url: String,
    stream: TcpStream,
}

impl Connection {
    fn method_that_uses_stream(&self) {
        // can use self.stream here
    }
}

这意味着您不必在代码中乱扔乱码来检查流是否已设置。

另请参阅:

  • How to write an idiomatic build pattern with chained method calls in Rust?