How do you get rust to recognize a module within a crate? I believed it was enough to declare mod [module name] in lib.rs. Is it not?
Error:
error[E0432]: unresolved import `crate::a`
--> src/bin/b.rs:2:12
|
2 | use crate::a::f;
| ^ could not find `a` in the crate root
src/a.rs:
pub fn f() {}
src/bin/b.rs:
use crate::a::f;
fn main() {}
src/lib.rs:
mod a;
Cargo.toml:
[package]
name = "m"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "b"
path = "src/bin/b.rs"
[dependencies]
You are confusing crates and packages. As per the corresponding chapter in the book:
A crate is the smallest amount of code that the Rust compiler considers at a time. [...] A crate can come in one of two forms: a binary crate or a library crate.
This means that your lib.rs and bin/b.rs files define two separate crates and thus crate refers to different things. Both belong to the same package which is defined by your Cargo.toml file. To use functions from your library crate in your binary crate, use the crate name instead of crate. In your case, the crate name is the same as the package name, which is m. Note that you will have to mark your library items as pub to use them in another crate.
An example of this can be found in the bat program, which uses both crate and bat in the imports of the bat binary crate:
https://github.com/sharkdp/bat/blob/2dbc88d3afdacf6449f299c70f29e5456904779b/src/bin/bat/main.rs
When a binary is compiled it is seen as the root of the project structure. So lib.rs is not compiled for binary targets and you can not use any of its modules unless you also add them in your binary. The most common approach is to put your binaries in the src folder with lib.rs so you don't need to worry about the folder structure when using your modules. I suppose you could use include!("lib.rs"); at the top of your binary to make it inherit everything from your library, but I have not tried it and it would likely be considered bad form.
However, you can sidestep the issue by making your binary depend on your library. The easiest way to do this is by making your binary its own crate which depends on your library.
There are also some ways to get around this, is by making a crate a dependency of itself, but these solutions always make me feel a little uneasy about the stability of the solution. This answer can give you a general idea of what that looks like.
Here are some examples various solutions. In case you are wondering, these are just the first crates I found that fit the design pattern. I do not know what some of them do/contain.
https://github.com/stanislav-tkach/os_info (Workspace members)
https://github.com/sagiegurari/cargo-make (Binaires in source)
https://github.com/hominee/dyer (Subcrates (dyer-macros))
https://github.com/clap-rs/clap (Example binaries)
Related
I understand that one can import functionality within the project in 3 different ways:
Using project name
use proj_name::mod1::mod2::fn_name;
Using crate
use crate::mod1::mod2::fn_name;
Using a relative path
use mod1::mod2::fn_name;
What I'm confused about is that it seems that sometimes the compiler wants me to use (1) and sometimes (2) / (3). I can't figure out the ruleset for which should be used when. Can someone help?
Only the crate itself is allowed to refer to itself as The crate. Every single dependant uses its name prefix. That includes binary targets within the same crate. They might be coupled together in the project, but they are NOT part of library crate.
Binary targets in fact are all separate crates for all purposes of the build system that work the same as libraries that have lib.rs at their root and writing crate in them refers to root of that specific binary target.
Demo:
#[derive(Debug)]
pub struct A;
mod b {
#[derive(Debug)]
pub struct B(pub crate::A);
}
fn main() {
println!("{:?}", b::B(A));
}
Playground
There's a crate I want to use as a library for some of my own code (speedtest-rs specifically, but it doesn't really matter). However, whenever I try to use this crate, the compiler doesn't want to play nice with it.
$ cargo build
Compiling my-project v0.1.0 (/home/nick/Documents/code/my-project)
error[E0432]: unresolved import `speedtest_rs`
--> src/main.rs:1:5
|
1 | use speedtest_rs::*;
| ^^^^^^^^^^^^ use of undeclared type or module `speedtest_rs`
Looking at the Rust book, it seems like there's a distinction between a binary and library crae
The rand crate is a library crate which contains code intended to be used in other programs
Some googling has shown me that binary crates just have an extra link step, so I should be able to link against them, right? I know a lot of Rust packages have both a library and a binary in them, but what do you do when an author does not seem to follow this pattern?
Some googling has shown me that binary crates just have an extra link step, so I should be able to link against them, right?
No. It's not that simple. Plus that extra step creates an executable file rather than a library file. An executable cannot be used as a library.
I know a lot of Rust packages have both a library and a binary in them, but what do you do when an author does not seem to follow this pattern?
You can:
Ask them on GitHub to publish a library.
Fork the crate and make your own library (which you can do since it is published with the usual dual “Apache License, Version 2.0” + “MIT” license).
There isn't an automated way to use a binary crate as a library because in particular:
Rust won't generate a library.
Since the crate is missing a src/lib.rs file, nothing is exported. This is akin to have all items in that crate private. You wouldn't be able to use anything.
I have a crate foo_sys. In Rust 2015 I used extern crate foo_sys as foo for convenience, but in Rust 2018 extern crate isn't needed anymore and I don't want to use it only for aliasing. When dropping extern crate, I get
error[E0463]: can't find crate for foo
This can be achieved with the rename-dependency Cargo feature, available in Rust 1.31. With this feature, it's possible to provide a package attribute to the dependencies:
The rename-dependency feature allows you to import a dependency with a different name from the source. This can be useful in a few scenarios:
Depending on crates with the same name from different registries.
Depending on multiple versions of a crate.
Avoid needing extern crate foo as bar in Rust source.
Instead of writing
[dependencies]
foo_sys = "0.2"
the package key can be added to the dependency in Cargo.toml:
[dependencies]
foo = { package = "foo_sys", version = "0.2" }
WARNING: Cargo prior to Rust 1.26.0 may download the wrong dependency when using this feature!
The idiomatic solution is to rename the crate in Cargo.toml. See the answer by Tim Diekmann for more information about that.
But if you don't want to use Cargo.toml renaming for some reason, you can still use the old syntax. It's soft-deprecated, but not removed. So this still works:
extern crate foo_sys as foo;
(Playground example)
With the core library stabilized in Rust 1.6, the following becomes possible, and I do not need to replace libcore with libstd any more:
//extern crate core; //won't work without this line
extern crate num;
use core::ops::Add;
use num::bigint::{BigInt};
fn main() {
let mut big = "8705702225074732811211966512111".parse::<BigInt>().unwrap();
let one = "1".parse::<BigInt>().unwrap();
big = big.add(&one);
println!("{:?}", big);
}
But there is one thing puzzles me - why do I need to declare "extern crate core;"? As far as I know, libstd is meant to be built on top of libcore. libcore is meant to be OS independent, while the implementation of libstd can be OS specific. I never had the need to specify "extern crate std". What also puzzles me is that I do not need to add libcore as a dependency in Cargo.toml in the above case, although it is an extern crate.
Is libcore the only such case? Is this a temporary thing while the implementation of the language is getting stabilized?
In fact everything works in a pretty logical way.
First of all, libstd crate is indeed special. Rust compiler knows about it and it injects extern crate std; implicitly unless #![no_std] attribute is present on the crate root. Additionally it also imports standard prelude in every module of your crate (again, unless #![no_std] is present).
Now you can probably see why you have to specify extern crate core; while at the same time you don't need to specify extern crate std;. You also don't need to specify core in Cargo.toml because libcore, as well as several other libraries (libcollections, liballoc, liblibc, etc.; you can find an up-to-date list in Rust source directory) are present in the Rust compiler distribution. There is in fact a desire to allow these libraries to be available through Cargo as well (expressed in form of RFCs), but as of now these libraries are provided with the compiler distribution only.
And finally, remember that Rust crates are independent. Rust ABI is designed in such a way so you can have different versions of the same crate built into the final executable. While it is invalid for one crate to directly depend on multiple versions of the same crate, it is possible that its dependencies transitively depend on different versions of another crate. This is one of the reasons why you always have to explicitly specify dependencies of your crate: if you don't specify that your crate depends on libcore, even though libstd does depend on libcore, libcore won't be pulled by your crate automatically if it uses libstd.
I think that use is used to import identifiers into the current scope and extern crate is used to declare an external module. But this understanding (maybe wrong) doesn't make any sense to me. Can someone explain why Rust has these two concepts and what are the suitable cases to use them?
extern crate foo indicates that you want to link against an external library and brings the top-level crate name into scope (equivalent to use foo). As of Rust 2018, in most cases you won't need to use extern crate anymore because Cargo informs the compiler about what crates are present. (There are one or two exceptions)
use bar is a shorthand for referencing fully-qualified symbols.
Theoretically, the language doesn't need use — you could always just fully-qualify the names, but typing std::collections::HashMap.new(...) would get very tedious! Instead, you can just type use std::collections::HashMap once and then HashMap will refer to that.
Since Rust 2018 you're only required to add external dependencies to your Cargo.toml, so you no longer need to use extern crate foo.
use works the same as before.
Read more in the official documentation.