How come Rust has trouble finding transitive dependencies? [duplicate] - rust

This question already has answers here:
Why we need to specify all dependencies (including transitives) in Rust?
(1 answer)
How to properly reference the same code as a dependency of a dependency?
(2 answers)
Closed 10 months ago.
In Rust I'm getting the error during cargo build:
use tokio::net::{TcpListener, TcpStream};
^^^^^ use of undeclared crate or module `tokio`
even though I have
[dependencies]
tokio-tungstenite = {version = "0.17", features = ["native-tls"]}
and I can see the tokio in the cargo tree
└── tokio-tungstenite v0.17.1
├── tokio v1.18.1
Is Rust not pulling in the transitive dependencies? I thought it would.

This is by design. You have to add tokio to your dependencies for it to be available. Under the hood cargo will attempt to re-use the same tokio version, but it is not always possible to do so.
For example lets say your dependencies look like this:
[dependencies]
foo = "0.4.9" # Depends on sap = "0.2.1"
bar = "1.2.1" # Depends on sap = "0.9.7"
These two crates depend on different versions of sap. Since cargo is unable to find a version that both crates are happy with, it builds both. At this point it is completely ambiguous which version cargo should supply your crate.
However, you can use * to instead ask cargo to choose a version for you.
sap = "*"
You can read more about how versions are handled here:
https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html

Cargo does pull transitive dependencies, but it doesn't mean you can access them. You have to explicitly declare that you want it to be a "direct" dependency of your crate for it to be accessible.

Related

Cannot find [package] in the crate root

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)

How to override Cargo crate Dependencies of two different versions of crate using patch.crates-io?

I am trying to patch problematic crate that is included indirectly by crates that I include in my project. But issue is, I see two different crates use two different versions of problematic crate. Using patch.crates-io, I could patch just one version of problematic crate.
To explain my problem, here is the code illustration.
Lets say I have a project called 'myproject' which includes Crate 'A' and Crate 'B'. Both Crate 'A' and Crate 'B' includes Crate 'C' but two different version (Lets say 0.1.1 and 0.2.2). But crate 'C' needs some customization to fix some issues that is affecting my project. After making local copies of both versions, I made some fixes and now I want to update myproject with patch.crates-io like below. But I could provide patch to only one version of Crate 'C'.
In myproject.toml
[patch.crates-io]
C = { version = "0.1.1", path = "../mylocalpath/C" }
So question is, is there a way of patching both versions of crate 'C' without making local copies of crate 'A' or crate 'B'?

Linking against binary crate

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.

How can I use the futures 0.1 and futures-preview 0.3 crates at the same time? [duplicate]

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)

How do I add #![feature(***)] to another crate's attributes without forking the crate?

Every time I see an error like:
error: associated constants are experimental (see issue #29646)
...
= help: add #![feature(associated_consts)] to the crate attributes to enable
= note: this error originates in a macro outside of the current crate
I fork the crate and add the feature to the crate attributes and then replace the dependency in my Cargo.toml:
[replace."bitflags:1.0.0"]
git = "https://github.com/boehm-s/bitflags"
rev = "bb2afce"
Is there a way to get rid of these errors without forking crates?
Associated constants were stabilized recently in Rust 1.20; this error should disappear without any code changes if you upgrade your Rust compiler.
In addition to [replace] and [patch], there's also the option of using .cargo/config to override a dependency locally without changing Cargo.toml.
How do I add #![feature(***)] to another crate's attributes without forking the crate?
You don't. You cannot change another crate without... changing it.
You are using an old nightly compiler; update it. If you are using a nightly compiler, it's your responsibility to keep it up-to-date. If you don't know why you have a nightly compiler, switch to a stable compiler.
This can only occur if:
You are using a nightly compiler. If you weren't, you wouldn't get the suggestion to add the attribute, it'd be a hard error.
You are using a crate that relies on a feature that has not been stabilized in your version of the compiler and thus requires the attribute to enable it.
The feature has been stabilized in a newer version of Rust and thus the crate no longer needs to have the attribute itself.
For this example, you can read the crates changelog:
1.0.0
[breaking change] Macro now generates associated constants (#24)
[breaking change] Minimum supported version is Rust 1.20, due to usage of associated constants

Resources