Unresolved import in Rust. Can't find crate [duplicate] - rust

I have 4 files:
main.rs
mod bar;
fn main() {
let v = vec![1, 2, 3];
println!("Hello, world!");
}
lib.rs
pub mod foo;
pub mod bar;
foo.rs
pub fn say_foo() {
}
bar.rs
use crate::foo;
fn bar() {
foo::say_foo();
}
When I run cargo run I get an error saying:
error[E0432]: unresolved import `crate::foo`
--> src/bar.rs:1:5
|
1 | use crate::foo;
| ^^^^^^^^^^ no `foo` in the root
Could someone explain to me how to fix this? A bit more broadly: how does module lookup work when there's a main.rs and a lib.rs?
Edit: Adding mod foo to main.rs fixes the issue. But I don't understand this -- I was under the impression the lib.rs was the place that "exposed" all of my modules? Why do I have to declare the module in main.rs as well?
My Cargo.toml:
[package]
name = "hello-world"
version = "0.1.0"
authors = ["me#mgail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

Let's start from the beginning. Look at the Package Layout chapter in The Cargo Book. As you can see, your package can contain lot of stuff:
a binary (something you can run) or multiple binaries,
a single library (shared code),
example(s),
benchmark(s),
integration tests.
Package layout
Not all of the possibilities are listed here, just the binary / library combinations.
A binary
This is an example of a package with single binary. Entry point is the main function in the src/main.rs.
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
src/main.rs:
fn main() {
println!("Hallo, Rust here!")
}
$ cargo run
Hallo, Rust here!
A library
This is an example of a package with a library. Libraries don't have entry points, you can't run them. They're used for functionality sharing.
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
src/lib.rs:
pub fn foo() {
println!("Hallo, Rust library here!")
}
$ cargo run
error: a bin target must be available for `cargo run`
Do you see anything in the Cargo.toml file about a binary or a library? No. The reason is that I've followed the Package Layout and the cargo knows where to look for things.
A binary and a library
This is an example of a package with a binary and a library.
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
src/lib.rs:
pub const GREETING: &'static str = "Hallo, Rust library here!";
src/main.rs:
use hallo::GREETING;
fn main() {
println!("{}", GREETING);
}
Same question, do you see anything in the Cargo.toml file about a binary or a library? No.
This package contains two things:
a binary (root src/main.rs, entry point src/main.rs::main),
a library (root src/lib.rs, shared code).
A library can be referenced from the binary via use hallo::... where the hallo is this package name (Cargo.toml -> [package] -> name).
Your problem
Cargo.toml:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
Same package layout
A library part
src/lib.rs:
pub mod bar;
pub mod foo;
src/foo.rs:
pub fn say_foo() {
println!("Foo");
}
src/bar.rs:
use crate::foo;
pub fn bar() {
foo::say_foo();
}
crate refers to src/lib.rs, because we're in the context of our library here.
Treat it as a standalone unit and refer to it via use hallo::...; from the outside world.
A binary part
src/main.rs:
use hallo::bar::bar;
fn main() {
bar();
}
Here we're just using our library.
Without a library
Same code, but lib.rs was renamed to utils.rs and (foo|bar).rs files were moved to the src/utils/ folder.
src/utils.rs:
pub mod bar;
pub mod foo;
src/utils/foo.rs:
pub fn say_foo() {
println!("Foo");
}
src/utils/bar.rs:
use super::foo;
// or use crate::utils::foo;
pub fn bar() {
foo::say_foo();
}
We can use crate here as well, but because we're in the context of our binary, the path differs.
src/main.rs:
use utils::bar::bar;
mod utils;
fn main() {
bar();
}
Here we just declared another module (utils) and we're using it.
Summary
Cargo.toml content:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
If there's a src/main.rs file, you're basically saying this:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
[[bin]]
name = "hallo"
src = "src/main.rs"
If there's a src/lib.rs file, you're basically saying this:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
[lib]
name = "hallo"
path = "src/lib.rs"
If there're both of them, you're basically saying this:
[package]
name = "hallo"
version = "0.1.0"
edition = "2018"
[[bin]]
name = "hallo"
path = "src/main.rs"
[lib]
name = "hallo"
path = "src/lib.rs"
Documentation
Package Layout
The Manifest Format
Managing Growing Projects with Packages, Crates, and Modules

In short the official Rust book has this to say:
If a package contains src/main.rs and src/lib.rs, it has two crates: a library and a binary, both with the same name as the package.
Furthermore the Rust reference says this:
crate resolves the path relative to the current crate
So there are actually two crates in your project, and to which crate the crate qualifier resolves to depends on where you call it.
Now in your code example, if you want things to compile you have to remove mod bar; from src/main.rs. Otherwise you'll be declaring that bar is a module within two crates.
After you remove that, then because in src/lib.rs you had:
pub mod foo;
pub mod bar;
bar would now be a module within src/lib.rs's crate, so the crate qualifier in bar.rs would then refer to src/lib.rs's hello-world crate, which is what you want.
One more thing, if you wanted to access items that are exposed in src/lib.rs from src/main.rs, you have to do as #zrzka said, which is to name the name of the crate that both src/lib.rs and src/main.rs share. For example, in your project which is named hello-world:
use hello_world::foo;
fn main() {
foo::say_foo();
}
is how you import the foo module declared in src/lib.rs into src/main.rs.
However it does appear that the importing behavior doesn't work the other way. I.e. if you declare some public module in src/main.rs, you can't import it into the src/lib.rs crate even when you specify the name of the crate. I couldn't find documentation describing this behavior but by testing it in Rust 1.37.0, it does appear to be the case.

The lib.rs and main.rs files are two independent entry points for your package.
When you use cargo run (or build the binary and run it explicitly), the entry point to be used is main.rs, and the crate keyword refer to the binary crate. It doesn't even have to know that there is something in lib.rs: the binary will treat the library as it would any other external crate, and it must be imported, through extern crate hello_world or, for example, use hello_world::foo.
When you import the library, however, the entry point is lib.rs, and the crate is the library crate. In this case, yes, all that you've added to lib.rs is exposed to the whole crate.
The usual worksflow in this case is to make the binary something like a thin wrapper around the library - in some extreme cases the main.rs would only contain something like
fn main() {
library::main();
}
and the whole logic (and all the project structure) goes into the library crate. One of the reasons is exactly what you've run into: the possible confusion whether this concrete module is imported in each crate in the package.

One more thing: mod defines a new module in your crate, whether a binary crate or library crate; while use only brings the module into the current scope.
In your example, use crate::foo in bar.rs tries to bring a module named foo under crate root into scope. But because there is no mod foo in main.rs, the foo module is not part of the binary crate.

Related

Why I cannot find the compiled wasm file?

I am new to the WASM world. I tried to create a really simple rust lib that only contains add function and wants to compile it to the .wasm. I already know that the wasm-pack can achieve this. But why the cargo build --target wasm32-unknown-unknown --release can not?
// Cargo.toml
[package]
name = "hello-wasm"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
// lib.rs
pub fn add(left: usize, right: usize) -> usize {
left + right
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_works() {
let result = add(2, 2);
assert_eq!(result, 4);
}
}
The compiled target folder:
$ ls target/wasm32-unknown-unknown/release/
build/ deps/ examples/ incremental/ libhello_wasm.d libhello_wasm.rlib
Expected there is a file named hello-wasm.wasm.
With a lib.rs and no customization, you've defined a Rust library. By default, Cargo compiles libraries to .rlib files which can then be used as inputs when compiling crates that depend on this one.
In order to get a .wasm file, you must request a crate-type of cdylib in your configuration (and this is also standard when using wasm-pack):
[lib]
crate-type = ["cdylib"]
If you want the library to also be usable in tests and as a dependency, then you should specify the default mode "lib" too:
[lib]
crate-type = ["lib", "cdylib"]
(cdylib stands for "C dynamic library" — a WASM module is kind of like a dynamic library, and "C" should be understood as just meaning "native format, no Rust-specific conventions".)

How to benchmark rust library with cargo features?

I am working on a library that is configurable with cargo features, and I can't figure out how to get the bencher crate (for benchmarking) to work. In lib.rs I have
#[cfg(feature = "single_threaded")]
pub fn my_function() {...}
In benches/bench.rs I have
#[macro_use]
extern crate bencher;
extern crate my_crate;
use bencher::Bencher;
use my_crate::*;
fn single_thread(bench: &mut Bencher) {
bench.iter(|| {
for _ in 0..10 {
my_function();
}
})
}
benchmark_group!(benches, single_thread);
benchmark_main!(benches);
As is, the compiler says that it cannot find my_function because I haven't specified a configuration. If I add #[cfg(feature = "single_threaded")] above fn single_thread(), it can then find my_function, but that seems to put single_thread in a different context from everything else, such that the two macros at the bottom cannot find single_thread().
If I add #[cfg(feature = "single_threaded")] above each of the two macros, the compiler says to "consider adding a main function to benches/bench.rs," but a main function is added by benchmark_main!. If I put the entire file into a module and declare #[cfg(feature = "single_threaded")] once for the whole module, I get the same error about not having a main function. Any suggestions?
Oh and my Cargo.toml looks like this
[package]
name = "my_crate"
version = "0.1.0"
edition = "2021"
authors = ["Me"]
[dependencies]
[dev-dependencies]
bencher = "0.1.5"
[features]
single_threaded = []
[[bench]]
name = "benches"
harness = false

structopt unable to find its Args parser when defined in a separate file

I'm starting on a command-line tool in Rust, and hitting a wall right from the get-go. I can parse command-line arguments using StructOpt if the Opt struct is defined in main.rs, but since I want to be able to pass the Opt struct into the library code, I'm defining it in its own file so that other parts of the library know what it's all about.
Here's the version of the code that's dumping the fewest errors, although I concede that it's largely cobbled together by trying things suggested by the compiler and some random SO suggestions, so it may be completely wrong.
The build error I'm getting is:
$ cargo run
Compiling basic v0.1.0 (/home/mpalmer/src/action-validator/blobble)
error[E0433]: failed to resolve: maybe a missing crate `structopt`?
--> src/opt.rs:8:5
|
8 | /// Activate debug mode
| ^^^^^^^^^^^^^^^^^^^^^^^ not found in `structopt::clap`
|
help: consider importing this struct
|
3 | use opt::structopt::clap::Arg;
|
For more information about this error, try `rustc --explain E0433`.
error: could not compile `basic` due to previous error
$ cargo --version
cargo 1.56.0 (4ed5d137b 2021-10-04)
$ rustc --version
rustc 1.56.0 (09c42c458 2021-10-18)
(Yes, I have tried adding use opt::structopt::clap::Arg;, just in case, but the error doesn't go away and I get a warning about an unused import -- while still being told to try adding the same use that is unused, which is amusing)
Cargo.toml
[package]
name = "basic"
version = "0.1.0"
authors = ["User"]
[dependencies]
structopt = "0.3"
src/main.rs
extern crate basic;
extern crate structopt;
use basic::Opt;
use structopt::StructOpt;
fn main() {
let opt = Opt::from_args();
println!("{:#?}", opt)
}
src/lib.rs
mod opt;
pub use crate::opt::Opt;
src/opt.ts
extern crate structopt;
use self::structopt::StructOpt;
#[derive(StructOpt, Debug)]
#[structopt(name = "basic")]
pub struct Opt {
/// Activate debug mode
#[structopt(short,long)]
debug: bool,
}
Suggestions gratefully appreciated.
A working version is
Cargo.toml
[package]
name = "basic"
version = "0.1.0"
authors = ["User"]
edition = "2018"
[dependencies]
structopt = "0.3"
lib.rs
#[macro_use] extern crate structopt;
use structopt::StructOpt;
pub mod opt;
opt.rs
#[derive(StructOpt, Debug)]
#[structopt(name = "basic")]
pub struct Opt {
/// Activate debug mode
#[structopt(short,long)]
debug: bool,
}
main.rs
#[macro_use] extern crate structopt;
use structopt::StructOpt;
fn main() {
let opt = basic::opt::Opt::from_args();
println!("{:#?}", opt);
}
You need to declare use structopt::StructOpt because from_args trait must be in the scope.

How does thread_local! work with dynamic libraries in rust?

As the title indicates I'm confused as to how shared libraries work with thread locals in rust. I have a minimal example below:
In a crate called minimal_thread_local_example:
Cargo.toml:
[package]
name = "minimal_thread_local_example"
version = "0.1.0"
edition = "2018"
[dependencies]
has_thread_local = {path ="./has_thread_local"}
libloading = "0.5"
[workspace]
members = ["shared_library","has_thread_local"]
src/main.rs:
extern crate libloading;
use libloading::{Library, Symbol};
use has_thread_local::{set_thread_local, get_thread_local};
fn main() {
let lib = Library::new("libshared_library.so").unwrap();
set_thread_local(10);
unsafe {
let func: Symbol<unsafe extern fn() -> u32> = lib.get(b"print_local").unwrap();
func();
};
println!("From static executable:{}", get_thread_local());
}
In a crate called has_thread_local:
Cargo.toml:
[package]
name = "has_thread_local"
version = "0.1.0"
edition = "2018"
[lib]
[dependencies]
src/lib.rs:
use std::cell::RefCell;
use std::ops::Deref;
thread_local! {
pub static A_THREAD_LOCAL : RefCell<u64> = RefCell::new(0);
}
pub fn set_thread_local(val: u64) {
A_THREAD_LOCAL.with(|refcell| { refcell.replace(val); })
}
pub fn get_thread_local() -> u64 {
A_THREAD_LOCAL.with(|refcell| *refcell.borrow().deref())
}
In a crate called shared_library:
Cargo.toml:
[package]
name = "shared-library"
version = "0.1.0"
edition = "2018"
[lib]
crate-type = ["cdylib"]
[dependencies]
has_thread_local = {path = "../has_thread_local"}
src/lib.rs:
use has_thread_local::get_thread_local;
#[no_mangle]
unsafe extern "system" fn print_local() {
println!("From shared library:{}",get_thread_local());
}
Here's a github link for the above.
In essence I have a static executable and a shared library, with a thread local variable declared in the static executable. I then set that variable to 10 and access it from the shared library and static executable.
This outputs:
From shared library:0
From static executable:10
I'm confused as to why this is outputted(occurs on both stable and nightly). I would have imagined that both would be 10, since the thread local is declared in a static executable and is only accessed via functions also located in that static executable. I'm looking for an explanation as to why I am observing this behavior, and how to make my thread local have the same value across the entire thread, aka have the same value in the shared library and static library.
The reason this behavior is observed is because the shared library contains it's own copy of the code of crates it depends on, resulting in two different thread local declarations.
The solution to this is to pass a reference to the thread local in question, instead of directly accessing the thread local. See here for more information on how to obtain a reference to a thread local: How to create a thread local variable inside of a Rust struct?

How do I specify the linker path in Rust?

I'm trying to link a Rust program with libsoundio. I'm using Windows and there's a GCC binary download available. I can link it like this if I put it in the same folder as my project:
#[link(name = ":libsoundio-1.1.0/i686/libsoundio.a")]
#[link(name = "ole32")]
extern {
fn soundio_version_string() -> *const c_char;
}
But I really want to specify #[link(name = "libsoundio")] or even #[link(name = "soundio")], and then provide a linker path somewhere else.
Where can I specify that path?
I tried the rustc-link-search suggestion as follows:
#[link(name = "libsoundio")]
#[link(name = "ole32")]
extern {
fn soundio_version_string() -> *const c_char;
}
And in .cargo/config:
[target.i686-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/i686"]
rustc-link-lib = ["libsoundio.a"]
[target.x86_64-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/x86_64"]
rustc-link-lib = ["libsoundio.a"]
But it still only passes "-l" "libsoundio" to gcc and fails with the same ld: cannot find -llibsoundio. Am I missing something really obvious? The docs seem to suggest this should work.
As stated in the documentation for a build script:
All the lines printed to stdout by a build script [... starting] with cargo: is interpreted directly by Cargo [...] rustc-link-search indicates the specified value should be passed to the compiler as a -L flag.
In your Cargo.toml:
[package]
name = "link-example"
version = "0.1.0"
authors = ["An Devloper <an.devloper#example.com>"]
build = "build.rs"
And your build.rs:
fn main() {
println!(r"cargo:rustc-link-search=C:\Rust\linka\libsoundio-1.1.0\i686");
}
Note that your build script can use all the power of Rust and can output different values depending on target platform (e.g. 32- and 64-bit).
Finally, your code:
extern crate libc;
use libc::c_char;
use std::ffi::CStr;
#[link(name = "soundio")]
extern {
fn soundio_version_string() -> *const c_char;
}
fn main() {
let v = unsafe { CStr::from_ptr(soundio_version_string()) };
println!("{:?}", v);
}
The proof is in the pudding:
$ cargo run
Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
Running `target\debug\linka.exe`
"1.0.3"
Ideally, you will create a soundio-sys package, using the convention for *-sys packages. That simply has a build script that links to the appropriate libraries and exposes the C methods. It will use the Cargo links key to uniquely identify the native library and prevent linking to it multiple times. Other libraries can then include this new crate and not worry about those linking details.
Another possible way is setting the RUSTFLAGS like:
RUSTFLAGS='-L my/lib/location' cargo build # or cargo run
I don't know if this is the most organized and recommended approach, but it worked for my simple project.
I found something that works OK: you can specify links in your Cargo.toml:
[package]
links = "libsoundio"
build = "build.rs"
This specifies that the project links to libsoundio. Now you can specify the search path and library name in the .cargo/config file:
[target.i686-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/i686"]
rustc-link-lib = [":libsoundio.a"]
[target.x86_64-pc-windows-gnu.libsoundio]
rustc-link-search = ["libsoundio-1.1.0/x86_64"]
rustc-link-lib = [":libsoundio.a"]
(The : prefix tells GCC to use the actual filename and not to do all its idiotic lib-prepending and extension magic.)
You also need to create an empty build.rs:
fn main() {}
This file is never run, because the values in .cargo/config override its output, but for some reason Cargo still requires it - any time you use links = you have to have build =, even if it isn't used.
Finally in main.rs:
#[link(name = "libsoundio")]
#[link(name = "ole32")]
extern {
fn soundio_version_string() -> *const c_char;
}

Resources