Search code examples
rustimplementation

How can I regroup two structures in rust?


I am quite new to Rust and I have just spent my afternoon on the following implementation problem in Rust.

Here are my different files :

Foo.rs

struct Foo {
 socket: TCPSocket
}

TCP.rs (the Option fields are simply not defined at the beginning so they are initialized to None)

pub struct TCPSocket{
 client_address: Option<SocketAddr>,
 server_address: SocketAddr,
 status: SocketStatus,
 stream: Option<TCPStream>,
 listener: Option<TCPListener>,
 type: SocketType, //Either Client or Server
}

impl TCPSocket{
 pub write(&self)
 pub read(&self)
 pub connect(&self)
 pub bind_and_listen(&self)
 pub accept(&self)
}

So now here is my problem : I find this implementation a bit bad. The type of the socket is contained in a field and there are some methods/attributes that should not be accessed if one have a client socket (like bind_and_listen and accept or client_address). How to improve this code ?

So at first, I thought that a good solution was to divide the structure into two : one for the client socket and another one for the server socket. However, some methods/attributes are shared by those two structures. After some researches on StackOverflow, the solution is to create a sub-struct called for example BaseSocket that will implement all the shared attributes/methods and have this sub-struct as a attribute in both the Client and the Server struct. However, if I do that, I need to create two types of Foo (one for the Client and one for the Server) and I do not want that.

The solution that appeared to me after that is to regroup the Client and Socket in an enum so that I can pass this enum to the Foo struct. What I do not really like about this solution is that the user of the Foo struct already knows what type of socket he has in Foo. When the program is run, two Foo are created, one with the Client socket and one with the Server socket and they are used in two different parts of the code. And so whenever he wants to use the methods of a socket, he will have to use match even though he knows which variant he is working with.
For example let us consider that the user has a Foo with a server socket and he wants to bind, listen and accept the connection. He will have to match the field socket and then call the different methods needed even though he already knows that he is working with a server. I find this really weird to use a match when one knows what he is working with.

I am not sure if I am explaining myself clearly but I just cannot find a perfectly logical way to implement this. I know that in other languages, I could just have a mother class called Socket with all the shared attributes/methods. Then, I could have two daughter-classes, one for Client and one for Server with the specific methods/attributes. With these, I would just have to give to Foo the type of the mother-class and because a daughter is also of type mother, it would work. But I cannot find such a logical way in Rust.

As I said I am quite new to this language, so maybe I am missing something but if anyone has a solution, please enlighten me :) .


Solution

  • In TCP, the Server socket is distinct from Client socket. These have different inputs and different functionalities.

    • Server needs a specific port that it must bind to.
    • Server will only listen for conections on the current host.
    • Client will only connect to a server at a specific host.
    • Client will need the address and port of the server that it needs to connect
    • A socket cannot perform both listening and connecting at the same time.

    For these reasons, the two functionalities must be treated diferent and hence must be implemented separately.

    This separation is followed in the libraries of all major programming languages, including Rust.

    Recommended approach: Maintain two distinct structs - one for TCP Server and the other for TCP Client