14

I am writing a TCP client and have a conn field in my client struct. My client implements two methods new to instantiate the struct and connect to open a connection to the server and set that as the value of the conn field

pub struct FistClient {
    addr: String,
    conn: TcpStream,
}

impl FistClient {
    pub fn new(ip: &str, port: &str) -> Self {
        FistClient {
            addr: String::from(ip) + ":" + &String::from(port),
            // conn: <some-defaullt-value>,
        }
    }

    pub fn connect(&mut self, ip: &str, port: &str) {
        let res = TcpStream::connect(&self.addr);
        match res {
            Ok(c) => self.conn = c,
            Err(_) => panic!(),
        }
    }
}

I want to set the conn field in the new method to some default value. In Go I can do something like conn: nil but it doesn't work here. I tried Default::default() too but that trait isn't implemented for TCPStream how should I set it to a default value?

John Kugelman
  • 349,597
  • 67
  • 533
  • 578
Palash Nigam
  • 1,653
  • 3
  • 14
  • 26

3 Answers3

22

There's no null in Rust (and no Null Pointer Exception, Rust is designed for safety).

You must either

1) use an option (i.e. a field of type Option<TcpStream>)

2) or better: return a result when building the struct

Here, the best option would probably be to connect from inside a function returning a Result<FistClient>, so that you don't have to check whether your struct has a valid stream.

I would do something like this:

pub struct FistClient {
    addr: String,
    conn: TcpStream,
}

impl FistClient {
    pub fn new(ip: &str, port: &str) -> Result<Self> {
        let addr = format!("{}:{}", ip, port);
        let conn = TcpStream::connect(&addr)?;
        Ok(FistClient { addr, conn })
    }
}

Side note: It's really preferable to not build your applications with calls to panic, even when you think you're just building a dirty draft. Handle errors instead.

Denys Séguret
  • 372,613
  • 87
  • 782
  • 758
  • How do you instantiate the object then? I am really new to Rust and am pretty lost, trying to do the same thing as you.. – d3Roux May 13 '22 at 16:27
  • 1
    @d3Roux You instantiate it with `my_client = FirstClient::new(ip, port)?;` (the `?` is often replaced by local error handling depending on the logic of the application). – Denys Séguret May 13 '22 at 20:03
11

In Rust, the idea of null is modelled with Option. You give a field the type Option<TcpStream> to indicate that it might not be there (None), or be a valid value (Some(TcpStream)).

pub struct FistClient {
    addr: String,
    conn: Option<TcpStream>,
}

impl FistClient {
    pub fn new(ip: &str, port: &str) -> Self {
        FistClient {
            addr: String::from(ip) + ":" + &String::from(port),
            conn: None,
        }
    }

    pub fn connect(&mut self, ip: &str, port: &str) {
        let res = TcpStream::connect(&self.addr);
        match res {
            Ok(c) => self.conn = Some(c),
            Err(_) => panic!(),
        }
    }
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
4

You will need to change your type to Option<TCPStream> if you would like to keep this call pattern. an Option expresses the possible lack of a value (I. e. null) with two enum variants: Some(_) and None.

Once you have this in place you can easily retrieve a mutable reference to the inner member by calling as_mut to retrieve an Option<&mut T>.

Sébastien Renauld
  • 19,203
  • 2
  • 46
  • 66