Reputation: 3330
When creating a struct in Rust it seems like it's difficult to create one without having all of the fields set. For example with the following code
struct Connection {
url: String,
stream: TcpStream
}
You aren't able to set url
without giving stream
as well.
// Compilation error asking for 'stream'
let m = Connection { url: "www.google.com".to_string() };
How are you able to create these references that might be Option<None>
until a later time?
The best I have found is using the Default
trait, but I'd rather not have to create the TcpStream
until a later time than when the struct is initialised. Am I able to do this with something like a Box
?
Upvotes: 24
Views: 20802
Reputation: 430681
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 Option
s:
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:
Upvotes: 7
Reputation: 1562
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).
Upvotes: 4
Reputation: 38586
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.
Upvotes: 17