How are you able to create partially initialised structs?

All fields indeed have to be initialized when creating the struct instance (there is no null in Rust) so all the memory is allocated. There is often a dedicated method (like new) that sets default values for fields which are supposed to be modified at a later stage.

I'd use the Box when you don't know the size of the field (like Vec does).


As an extension to Jorge Israel Peña's answer, you can use a builder. The builder has all the optional fields and produces the final value without 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
    }
}

This means that you don't have to litter your code with checks to see if the stream has been set yet.

See also:

  • How to initialize a struct with a series of arguments
  • Do Rust builder patterns have to use redundant struct code?
  • Is it possible to create a macro to implement builder pattern methods?
  • How to write an idiomatic build pattern with chained method calls in Rust?

One thing you can do is to wrap the TcpStream in an Option, i.e. Option<TcpStream>. When you first construct the struct, it'll be None, and when you initialize it you make it self.stream = Some(<initialize tcp stream>). Wherever you use the TCPStream, you'll have to check if it's Some, i.e. if it has already been initialized. If you can guarantee your behavior then you can just unwrap(), but it's probably better to make a check anyways.

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");
        }
    }
}

This is similar to what is done in Swift, in case you're familiar with that language.

Tags:

Rust