This question already has answers here:
How to move tests into a separate file for binaries in Rust's Cargo?
(5 answers)
How do I test private methods in Rust?
(2 answers)
How do I access exported functions inside a crate's "tests" directory?
(2 answers)
How to access functions from the main crate when writing integration tests?
(1 answer)
Closed 2 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
I have been trying to understand how to import functions for testing in Rust for hours with no success. I have a project structure that looks like this:
.
├── Cargo.lock
├── Cargo.toml
├── src
│ ├── main.rs
│ └── funcs
│ ├── mod.rs
│ └── hello.rs
└── tests
└── test_hello.rs
src/funcs/mod.rs:
pub mod hello;
src/funcs/hello.rs:
pub fn hello() {
println!("{}", "hello!");
}
src/main.rs:
mod funcs;
fn main() {
funcs::hello::hello(); // this works
}
src/tests/test_hello.rs
mod funcs; // this import does not work!
#[test]
fn add() {
assert_eq!(2 + 2, 4);
}
#[test]
fn hello_test() {
assert_eq!(funcs::hello::hello(), "hello");
}
How can I import public functions in src so that they can be used in my testing dir?
Create a src/lib.rs file to put most of the logic of your package into a library crate and export the funcs module there:
pub mod funcs;
Now use the library (which contains the module) from wherever you like. In your case, from both src/main.rs and tests/test_hello.rs:
use <crate>::funcs;
Replace <crate> with the name of your library crate which is the same as the package name and your root folder.
A Rust crate can contain a program and/or a library. Tests can only access a library, not a program (and only the public parts of the library). In your case you have only a program, so you can't have tests. In order for tests to work, you will need to:
Split you code into a program (in the main.rs file) and a library (in the lib.rs file).
Make sure that any part of the library that you want to use in the program is public.
Make sure that any part of the library that you want to test is also public.
In main.rs and in the tests, write use foo::hello to access the hello function, replacing foo with the name of your library.
If you want to split the code into modules, declare each module with pub mod mod_name in lib.rs, then import them with use foo::mod_name; in main.rs or in the tests.
Rust considers tests as part of a separate crate, so you have to put use your_crate_name::funcs; at the top of your tests, where your_crate_name is the crate name of your main package as defined in Cargo.toml.
Related
I'm trying to understand how to properly setup imports, with two goals in mind:
Every script should be compilable using rustc
Building via cargo is supported swell.
With absolute paths it seems to work.
I cannot get it to work with relative paths.
However it seems wrong to use absolute paths.
Whats the correct way to handle imports?
Lets take two examples into account:
1. Example
src
├── main.rs
├── root
│ └── nested.rs
└── root.rs
abs paths
// main.rs:
#[path="root/nested.rs"]
mod nested
#[path="root.rs"]
mod root
// root.rs:
#[path="root/nested.rs"]
mod nested
rel paths
// main.rs:
mod root;
use crate::root::nested;
// root.rs --\> cannot be compiled using `rustc`.
pub mod nested;
2. Example
src
├── main.rs
├── notnested.rs
└── root.rs
abs paths
// main.rs:
#[path="notnested.rs"]
mod notnested
#[path="root.rs"]
mod root
// root.rs:
#[path="notnested.rs"]
mod notnested
rel paths
// main.rs:
mod root
mod notnested
// root.rs --\> cannot be compiled using `rustc`.
use crate::notnested;
I've got a repo here: https://github.com/cadolphs/aoc_2022/tree/main/src
I used Rust for the Advent of Code 2022. In this project, I have the main.rs for the binary. I have individual modules for each day of the challenge.
Some of these modules are single-file modules (day1.rs) and other modules are in directories like day11. Note that the "entry point" to a module in a folder should be called mod.rs for the import to work normally.
This structure should work for what you're trying to do.
I have a custom binary called cli.rs and a file clap.rs with some utilities for clap.rs:
src
bin
cli.rs
clap.rs
where clap.rs just provides me with the clap definitions:
pub fn get_matches() -> ArgMatches {
}
fn main() {}
so I can import them into cli.rs.
However if I take out fn main from clap.rs I get
error[E0601]: `main` function not found in crate `clap`
I don't want clap.rs to have a main function, I just it to simply be a utils file for the binary cli.rs
From the cargo book about package layout:
Cargo uses conventions for file placement to make it easy to dive into a new Cargo package:
.
// ...
├── src/
| | // ...
│ └── bin/
│ ├── named-executable.rs
│ ├── another-executable.rs
│ └── multi-file-executable/
│ ├── main.rs
│ └── some_module.rs
| // ...
[...]
The default executable file is src/main.rs.
Other executables can be placed in src/bin/.
[...]
If a binary, example, bench, or integration test consists of multiple source files, place a main.rs file along with the extra modules within a subdirectory of the src/bin, examples, benches, or tests directory. The name of the executable will be the directory name.
By putting both cli.rs and clap.rs into bin/, you told Cargo that you have two binaries: One named cli, one named clap.
This package layout should work for you:
src
bin
cli
main.rs (that's your cli.rs)
clap.rs
Alternatively, you could also put clap.rs into a lib crate (by putting it inside lib/ and then putting pub mod clap; inside lib.rs). Note however that you then need to reference get_matches() as <your_package_name>::clap::get_matches() so that the compiler knows you are not referencing something from your binary crate, but something from your library crate.
I have a question about rust modules resolution.
It seems to be that inside crate modules I can reference the other modules using crate name or crate/super/self keywords.
but in main.rs I can only use modules with crate name?
Am I doing something stupid here?
My project:
$ tree
.
├── Cargo.toml
└── src
├── add2.rs
├── add.rs
├── lib.rs
└── main.rs
Cargo.toml content:
[package]
name = "example"
....
main.rs content:
use example::add::{add_one};
fn main() {
println!("{}", add_one(1));
}
lib.rs content:
pub mod add;
add.rs content:
pub fn add_one(x: i32) -> i32 {
x + 1
}
add2.rs content:
use crate::add::{add_one};
pub fn add_two(x: i32) -> i32 {
add_one(add_one(x))
}
Am I doing something stupid here?
No, it`s just that Cargo adds one more layer of complexity called the "package", for better or worse, which makes it more confusing.
In short, your Cargo package contains two Rust crates: the binary one and the library one. When you are in main.rs, the example library crate is an external dependency like any other... which means you aren`t in the same crate!
Crate roots such as main.rs, lib.rs, bin/other_root.rs, etc. are treated differently by the module system. When they refer to module files they are referring to files in the root of the src/ directory only. In the normal case mod refers to files in the root of a local sub-directory with the same name as the containing file, mirroring the unique container constraint of the inline module layout as well as the file system hierarchy (barring linked files, of course). In essence, crate roots treat src/ as their local sub-directory as far as mod is concerned.
I gave perhaps a more wordy explanation here
Two executables' sources, foo.rs and bar.rs, are located in src/bin.
Private common functionality exists in src/bin/common.rs.
foo.rs and bar.rs include this functionality with:
mod common;
use common::{Bish, Bash, Bosh};
This works, but src/bin/common.rs doesn't feel like the right path for something which isn't going to be built into an executable.
Moving common.rs to src or src/lib stops foo.rs and bar.rs from seeing it.
Where should I put common.rs and how do I then import it?
A common approach to store shared parts at lib.rs and use these in binaries. Its usage, though, is a bit different than simply mod + use. In fact, library is a separate crate, so you need to access it via crate name (defined in Cargo.toml).
Cargo.toml:
[package]
name = "crate-name"
# ... the rest
src/bin/foo.rs:
fn main() {
crate_name::fun();
}
src/lib.rs:
pub fn fun() {}
example
├── Cargo.toml
└── src
├── bin
│ ├── bar.rs
│ └── foo.rs
├── common.rs
└── lib.rs
foo.rs and bar.rs:
#[path = "../common.rs"]
mod common;
use common::{Bish, Bash, Bosh};
also see: How can I use a module from outside the src folder in a binary project, such as for integration tests or benchmarks?
I am learning Rust and decided to write a simple client/server program. Both the client and the server will be using a very simple module I've already written. Knowing that this code might grow, I decided to compartmentalize my source for clarity. Right now my current hierarchy looks as follows:
├── Cargo.lock
├── Cargo.toml
├── README.md
├── src
│ ├── client
│ │ └── main.rs
│ ├── common
│ │ ├── communicate.rs
│ │ └── mod.rs
│ ├── lib.rs
│ └── server
│ └── main.rs
Many of the examples I found on Stack Overflow and the net provide great samples for when the main.rs is in the project root directory. Unfortunately I'm trying to do something different as shown above.
communicate.rs contains all of the network code I have written. Eventually I will add other Rust files here and include their public mod statement in mod.rs. Currently common/mod.rs all I have is
pub mod communicate;
Focusing on just the client folder, all I have is main.rs as shown. The file "header" lists
extern crate common;
use std::thread;
use std::time;
use std::net;
use std::mem;
use common::communicate;
pub fn main() {
// ...
}
Besides the fundamental [package] section, all I have in Cargo.toml is
[[bin]]
name = "server"
path = "src/server/main.rs"
[[bin]]
name = "client"
path = "src/client/main.rs"
When I try to build the client binary, the compiler complains that the common crate could not be found.
$ cargo build
Compiling clientserver v0.1.0 (file:///home/soplu/rust/RustClientServer)
client/main.rs:1:1: 1:21 error: can't find crate for `common` [E0463]
client/main.rs:1 extern crate common;
^~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
error: Could not compile `clientserver`.
To learn more, run the command again with --verbose.
I think this is because it is looking for a common crate within the client/ folder. I had this same problem when I tried the mod statement instead of extern crate statement.
use std::thread;
use std::time;
use std::net;
use std::mem;
mod common;
Gave me:
client/main.rs:6:5: 6:11 error: file not found for module `common`
client/main.rs:6 mod common;
^~~~~~
client/main.rs:6:5: 12:11 help: name the file either common.rs or common/mod.rs inside the directory "client"
I also tried (using the extern crate...) adding a lib.rs in the client whose contents are pub mod common; but I still get the same error as the first.
One potential solution I found to model it like this project, but this would require a Cargo.toml in every folder, something which I'd like to avoid.
I feel like I am close but am missing something.
You are not building common as a crate right now. The crates being built are the library clientserver (the default name for the library is the package name) and the binaries client and server.
Normally, extern crate clientserver; should work. However, if you want to name your library differently, you can do so by specifying a different name in a [lib] section in Cargo.toml. In this section, you can also specify a different source path for the library's main source file. In your case, it will probably be better, otherwise you'll end up with a crate named common and all of its contents would be in a module named common, so you'd have to access everything as common::common::foo. For example, by adding this to your Cargo.toml:
[lib]
name = "common"
path = "src/common/lib.rs"
you could combine src/lib.rs and src/common/mod.rs into src/common/lib.rs. Then, extern crate common; should work in your binaries.