Module to encapsulate a communication

Hi there,

I am a newbie with RUST but I have experience with C/C++. I have a simple C++ library which do the communication to a server (simple client/server communication).
For learning RUST I want to write the library and a small main application in RUST.
The library I want to realize as a module (pub mod mylib {}) and "include" this with "extern crate mylib;" in the main.rs.
As far as good. I want to save the communication parameter (IP, port) inside the module with an initialization function. But how can I create global variables inside the module? I always get an error "expected identifier, found keyword" when I do the declaration outside of a function.

Thanks
Michael

What gives you that error, and what is the full error?

lib.rs
pub mod mylib {

let mut host_addr: String;
let mut host_port: i16;
let mut tcp_buffer: [char; 1030];

pub fn init() {
    println!("mylib init!");
}

}

error: expected identifier, found keyword let
--> src/lib.rs:3:9
|
3 | let mut host_addr: String;
| ^^^ expected identifier, found keyword

Globals are not created with the let keyword, but const or static.

However mutable globals are somewhat difficult to use due to multi-threading concerns. I would try to avoid them if at all possible.

1 Like

Hi alice,
thanks for your fast reply. How can I avoid the globals? What is the right way in RUST?

You can use arguments to functions or put the values in struct fields.

2 Likes

This error message could be improved: hint for global use of let instead of const or static · Issue #83265 · rust-lang/rust · GitHub

1 Like

To expand a little on Alice's answer, the main function will typically create various shared resources, such as communication channels, and data shared by Arc. When threads are created clones of the Arc pointers, or channel values, are moved into the closure, so the threads can use the channels to communicate or share data by Arc and possibly protected by Mutex or RwLock (if it is mutable).

Oh lord, it is very difficult to understand how to write a "library" in RUST. I think I have a general problem in understanding the structure of RUST.

What I want is to have client that communicates with an server. The communication protocol should encapsulate in a "library". What is the correct word for "library" in RUST? Module, Crate?

In C++ my library contains the code for the protocol and variables like IP, port, etc. Then I have a main.cpp where I do an instance of my library, call a init function with the arguments IP and port and then I can call functions like get_version that sends a request to the server and returns a version string.

How can I realize that in RUST?
My trials:

main.rs:

extern crate my_protocol;

use my_protocol::ep;

fn main() {
    println!("Hello ep!");
    let mut my_data = ep::EpData {
        host_addr: String::from("192.168.0.20"),
        host_port: 9885,
    };
    my_data.set_host_addr(String::from("192.168.0.50")); // only for testing
    my_data.set_host_port(9885);                                      // only for testing
    ep::init();
}

lib.rs:

pub mod ep {
    pub struct EpData {
        pub host_addr: String,
        pub host_port: i16,
    }
    impl EpData {
        pub fn set_host_addr (&mut self, host: String) {
            self.host_addr = host;
        }
        pub fn set_host_port (&mut self, port: i16) {
            self.host_port = port;
        }

    }
    pub fn init() {
        println!("Ep init!");

    }
}

I don't want to have the IP and port outside of my module. I want to save this values inside the module and access them inside the module. How can I realize that?

Looks like you want init to take a parameter

pub fn init( data: &EpData )

and then call init like this:

ep::init( &my_data );

Yes but the data is then only available in the init function, isn't it?

If I have a second function in lib.rs like:

fn get_version () -> String {
  //connect to server with IP and port
  //read from socket
  //return version string
}

The get_version function has no access to EpData

It depends on how you want to use your library. If it's specific to a single project, you probably want to write is as a module, in which case you'd just include my_protocol.rs in the project's src directory and write mod my_protocol; into main.rs.

If you want a reusable library that works with multiple projects, it needs to be an independent Cargo project with its own Cargo.toml, src directory, etc¹. A reference to this project goes into the [dependencies] section of Cargo.toml in each project that uses the library, and you write use my_protocol::ep; in their source files.

It's extremely rare to need an extern crate declaration in your source code these days; it's a holdover from before Cargo was fully developed.

¹ Cargo's "workspaces" feature can help manage this, but I won't go into that here.

Generally, for this kind of stuff, you should define a struct and define your methods on the struct. Then, any shared data can be fields of the struct.

Main would then call the methods by creating an instance of the struct and using the methods on it.

1 Like

Ah okay. I want a reusable library. So my first step is:

cargo new my_protocol --lib

And the main project:
cargo new my_test_app --bin

:+1:

Well it needs to be passed to every function or closure that needs it.

That means I have to add every function at least one argument EpData? Really?

Yes.

But you can do this:

pub mod ep {
    pub struct EpData {
        pub host_addr: String,
        pub host_port: i16,
    }
    impl EpData {
        pub fn set_host_addr(&mut self, host: String) {
            self.host_addr = host;
        }
        pub fn set_host_port(&mut self, port: i16) {
            self.host_port = port;
        }

        pub fn init(&mut self) {
            
        }
        pub fn get_version(&mut self) -> String {
            ...
        }
    }
}
1 Like

Thanks alice,
I think I got it and I am able to do the next steps on my own.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.