Rust project workspace improvement accessibility - rust

Problem
Is there a cleaner way to name imports from member projects then this:
(1) libs/src/messages/server_message.rs
...
let states = libs::messages::server_message::ServerMessage::Welcome(id);
...
e.g. this without changing the source code:
...
let states = libs::messages::ServerMessage::Welcome(id);
...
Project setup
Following workspaces approach.
My project looks like this
(2) Cargo.toml
...
[workspace]
members = [
"libs",
]
[dependencies]
libs = { path = "libs" }
...
(3) libs/src/libs.rs
pub mod messages;
(4) libs/src/messages/mod.rs
pub mod client_message;
pub mod remote_state;
pub mod server_message;
pub mod state;
(5) libs/src/messages/server_message.rs
...
pub enum ServerMessage {
Welcome(usize),
Goodbye(usize),
Update(Vec<RemoteState>),
}
...

Only changes required
(4) libs/src/messages/mod.rs
...
mod server_message;
pub use crate::messages::server_message::ServerMessage;
...
I could then use following import
(1) libs/src/messages/server_message.rs
let states = libs::messages::ServerMessage::Welcome(id);

Related

Rust bin accessing modules

Problem
Cannot access ServerConnection which is in /src/gameclient/server_connection.rs from /src/bin/client.rs.
I dont have any lib.rs nor main.rs all binaries are in /src/bin/
Project structure
Cargo.toml
...
[[bin]]
name = "server"
path = "src/bin/server.rs"
[[bin]]
name = "client"
path = "src/bin/client.rs"
...
/src/gameclient/mod.rs
rust-analyzer already complains here with:
file not included in module tree
pub mod player_instance;
pub use crate::gameclient::player_instance::PlayerInstance;
pub mod server_connection;
pub use crate::gameclient::server_connection::ServerConnection;
/src/gameclient/server_connection/ServerConnection
...
pub struct ServerConnection {
pub server_endpoint: url::Url,
}
pub impl ServerConnection {
pub fn connect(&self) {
...
Pitaj gave the correct answer already.
Just for completeness.
/src/bin/client.rs
adding this at the very top makes me get rid of lib.rs.
#[path = "../gameclient/mod.rs"]
mod gameclient;

Module resolution

I have the following code in src/
main.rs
a.rs
b.rs
Here's the code:
main.rs
mod a;
mod b;
use crate::a::Summary;
use crate::b::Person;
fn main() {
let p = Person{ first: "John".to_string(), last: "Doe".to_string() } ;
sum(p) ;
}
fn sum(summary: impl Summary) {
println!("{}", summary.summarize()) ;
}
a.rs
pub trait Summary {
fn summarize(&self) -> String ;
}
b.rs
use crate::Summary;
pub struct Person {
pub first: String,
pub last: String,
}
impl Summary for Person {
fn summarize(&self) -> String {
format!("{}, {}.", self.last, self.first)
}
}
What I don't understand is how does "use crate::Summary;" not cause a problem in b.rs? It should be "use crate::a::Summary;" or even "use super::a::Summary;", but for some reason use crate::Summary works. Is there some kind of funky search logic being applied here under the hood?
Items defined without a visibility specifier are available to the module that they're defined in and all of its sub-modules.
Since a and b are submodules of the crate root module, they can access the Summary object that was imported via a use declaration in main into the crate root module.

Why do I get "expected struct file1::A found struct file2::A" error when using a struct in multiple files?

I am trying to share a struct between two files, but I am getting an error.
I have the following folder structure:
src/
Models/
Login.rs
Routes/
LoginRoute.rs
Services/
LoginService.rs
main.rs
In Login.rs I have:
#[derive(Serialize, Deserialize, Debug)]
pub struct UserLoginResponse {
id: i32,
username: String,
token: String
}
In LoginRoute.rs I have:
#[path = "../Models/Login.rs"]
pub mod Login;
#[path = "../Services/LoginService.rs"]
pub mod LoginService;
#[post("/login", format = "application/json", data = "<user>")]
pub async fn login(user: String) -> Json<Login::UserLoginResponse> {
if let Ok(sk) = LoginService::callAuthenticate(user).await {
return sk
......
In LoginService.rs I have:
#[path = "../Models/Login.rs"]
pub mod Login;
pub async fn callAuthenticate(user: String)-> Result<Json<Login::UserLoginResponse>, Error> {
...
let userLoginResponse :Login::UserLoginResponse = Login::UserLoginResponse::new(1, "admin".to_string(), api_reponse.return_result);
Ok(Json(userLoginResponse))
}
I am getting error in LoginRoute.rs on the return sk line:
expected struct 'LoginRoute::Login::UserLoginResponse', found struct 'LoginService::Login:UserLoginResponse'
Please do not use the #[path = ...] attribute for your typical organization; it should only be used in obscure cases. Each time you do mod something, you are declaring a new module. Even if two modules point to the same file due to #[path = ...], they will be distinct.
So you have multiple UserLoginResponse structs declared:
one at crate::LoginRoute::Login::UserLoginResponse
one at crate::LoginService::Login:UserLoginResponse
and maybe another if you've also declared Login in main.rs.
Since they're in distinct modules, the Rust compiler sees them as different types, which is not what you want.
Just use the idiomatic way of declaring modules. If you want to keep your existing folder structure without intermediate mod.rs files, you can declare them all in main.rs like so:
mod Models {
pub mod Login;
}
mod Routes {
pub mod LoginRoute;
}
mod Services {
pub mod LoginService;
}
And then access them elsewhere via crate::Models::Login and whatnot.
See:
How do I import from a sibling module?
You've probably already run into warnings from the compiler trying to encourage a specific style: "module [...] should have a snake case name". Idiomatic file structure would typically look like this:
src/
models/
login.rs
mod.rs
routes/
login_route.rs
mod.rs
services/
login_service.rs
mod.rs
main.rs
Where main.rs would have:
mod models;
mod routes;
mod services;
And src/models/mod.rs (for example) would have:
pub mod login;

The trait `std::convert::From<cli::Opts>` is not implemented

I try to create a simple application parsing command line arguments using clap library and converting them to a Config custom structure. I implemented From trait for my structure, however, when I try to call from function, I receive the following error:
the trait bound `minimal_example::Config: std::convert::From<cli::Opts>` is not satisfied
the following implementations were found:
<minimal_example::Config as std::convert::From<minimal_example::cli::Opts>>
required by `std::convert::From::from`
Here is the code:
main.rs:
mod cli;
use clap::Clap;
use minimal_example::Config;
fn main() {
println!("Hello, world!");
let opts = cli::Opts::parse();
let config = Config::from(opts);
}
cli.rs:
use clap::{Clap, crate_version};
/// This doc string acts as a help message when the user runs '--help'
/// as do all doc strings on fields
#[derive(Clap)]
#[clap(version = crate_version!(), author = "Yury")]
pub struct Opts {
/// Simple option
pub opt: String,
}
lib.rs:
mod cli;
pub struct Config {
pub opt: String,
}
impl From<cli::Opts> for Config {
fn from(opts: cli::Opts) -> Self {
Config {
opt: opts.opt,
}
}
}
cargo.toml:
[package]
name = "minimal_example"
version = "0.1.0"
authors = ["Yury"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = {version="3.0.0-beta.2", features=["wrap_help"]}
What am I doing wrong?
You have added mod cli to both lib.rs and main.rs.
They are different from the standpoint of each other.
Rust modules confusion when there is main.rs and lib.rs
may help in understanding that.
That's what the error says. It's satisfied for std::convert::From<minimal_example::cli::Opts> but not for std::convert::From<cli::Opts>.
A simple fix:
main.rs
mod cli;
use clap::Clap;
use minimal_example::Config;
impl From<cli::Opts> for Config {
fn from(opts: cli::Opts) -> Self {
Config {
opt: opts.opt,
}
}
}
fn main() {
println!("Hello, world!");
let opts = cli::Opts::parse();
let config = Config::from(opts);
}
Now std::convert::From<cli::Opts> is implemented for Config.
How you actually want to place all this depends on your package architecture.

How do I access exported functions inside a crate's "tests" directory?

How do I access my libraries exported functions inside the create's "tests" directory?
src/relations.rs:
#![crate_type = "lib"]
mod relations {
pub fn foo() {
println!("foo");
}
}
tests/test.rs:
use relations::foo;
#[test]
fn first() {
foo();
}
$ cargo test
Compiling relations v0.0.1 (file:///home/chris/github/relations)
/home/chris/github/relations/tests/test.rs:1:5: 1:14 error: unresolved import `relations::foo`. Maybe a missing `extern crate relations`?
/home/chris/github/relations/tests/test.rs:1 use relations::foo;
^~~~~~~~~
If I add the suggested extern crate relations, the error is:
/home/chris/github/relations/tests/test.rs:2:5: 2:19 error: unresolved import `relations::foo`. There is no `foo` in `relations`
/home/chris/github/relations/tests/test.rs:2 use relations::foo;
^~~~~~~~~~~~~~
I want to test my relations in this separate tests/test.rs file. How can I solve these use issues?
Your problem is that, first, mod relations is not public so it is not visible outside of the crate, and second, you don't import your crate in tests.
If you build your program with Cargo, then the crate name will be the one you defined in Cargo.toml. For example, if Cargo.toml looks like this:
[package]
name = "whatever"
authors = ["Chris"]
version = "0.0.1"
[lib]
name = "relations" # (1)
And src/lib.rs file contains this:
pub mod relations { // (2); note the pub modifier
pub fn foo() {
println!("foo");
}
}
Then you can write this in tests/test.rs:
extern crate relations; // corresponds to (1)
use relations::relations; // corresponds to (2)
#[test]
fn test() {
relations::foo();
}
The solution was to specify a crate_id at the top of src/relations.rs:
#![crate_id = "relations"]
#![crate_type = "lib"]
pub fn foo() {
println!("foo");
}
This seems to declare that all the contained code is part of a "relations" module, though I'm still not sure how this is different to the earlier mod block.

Resources