Library compiles and runs tests, but fails compilation when used as a dependency

I'm using a readline library, rustyline, in my own project, without any compilation problems, on Linux. On Windows, I get compilation errors. Just to see if there was a problem with rustyline on Windows, I cloned the rustyline repo and cargo test there builds the library and successfully runs the tests. In my own project, whose Cargo.toml line just says

rustyline = "6"

I get compilation errors:

~\Projects\rsconfig [default]> cargo test                                                                                         
   Compiling rustyline v6.0.0                                                                                                     
error[E0432]: unresolved import `crate::tty::Terminal`                                                                            
  --> C:\Users\Vinay\.cargo\registry\src\github.com-1ecc6299db9ec823\rustyline-6.0.0\src\edit.rs:17:34                            
   |                                                                                                                              
17 | use crate::tty::{Renderer, Term, Terminal};                                                                                  
   |                                  ^^^^^^^^ no `Terminal` in `tty`                                                             
                                                                                                                                  
error[E0432]: unresolved import `crate::tty::Terminal`                                                                            
  --> C:\Users\Vinay\.cargo\registry\src\github.com-1ecc6299db9ec823\rustyline-6.0.0\src\keymap.rs:11:35                          
   |                                                                                                                              
11 | use crate::tty::{RawReader, Term, Terminal};                                                                                 
   |                                   ^^^^^^^^ no `Terminal` in `tty`                                                            
                                                                                                                                  
error[E0432]: unresolved import `crate::tty::Terminal`                                                                            
  --> C:\Users\Vinay\.cargo\registry\src\github.com-1ecc6299db9ec823\rustyline-6.0.0\src\lib.rs:46:43                             
   |                                                                                                                              
46 | use crate::tty::{RawMode, Renderer, Term, Terminal};                                                                         
   |                                           ^^^^^^^^ no `Terminal` in `tty`                                                    
                                                                                                                                  
error: cannot find macro `cfg_if` in this scope                                                                                   
   --> C:\Users\Vinay\.cargo\registry\src\github.com-1ecc6299db9ec823\rustyline-6.0.0\src\completion.rs:126:1                     
    |                                                                                                                             
126 | / cfg_if::cfg_if! {                                                                                                         
127 | |     if #[cfg(unix)] {                                                                                                     
128 | |         // rl_basic_word_break_characters, rl_completer_word_break_characters                                             
129 | |         const DEFAULT_BREAK_CHARS: [u8; 18] = [                                                                           
...   |                                                                                                                           
149 | |     }                                                                                                                     
150 | | }                                                                                                                         
    | |_^                                                                                                                         
    |                                                                                                                             
    = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace
for mo*emphasized text*re info)                                                                                                                    

and more in that vein. Since rustyline builds and runs cargo test fine (Rust 1.41.0 on Windows 10) I assume it could be something I'm doing wrong rather than a problem with rustyline. AFAIK I only need to declare direct dependencies in my Cargo.toml - and I'm confused to see errors such as the cfg_if error, as I'm not using the macro in my own code. The rustyline Cargo.toml declares the cfg_if crate as a dependency - I shouldn't need to, right?

Yep, it is not your fault, but rustyline's. The problem is very sneaky: rustyline depends on cfg-if with:

cfg-if = "0.1"

i.e., it requires a[ny] version of cfg-if that is >= 0.1.0 and < 0.2.0 (see https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html for more info).

And it turns out that this is not the case! I have tested it and rustyline requires at least cfg-if = "0.1.6" to compile properly.

When you run rustyline alone, without any set of constraints whatsoever (e.g. other dependencies or a Cargo.lock pin-pointing the dependencies' versions), cargo will look at the whole dependency tree, resolve the set of constraints, and among the underconstrained compatible crates (e.g., versions of cfg-if in the [0.1.0; 0.2.0[ interval), it will pick the last version available. In this case, it picks cfg_if = "0.1.10" (you can look at the Cargo.lock file in your clone of rustyline to verify it by yourself), which is indeed >= 0.1.6 and thus cargo test runs fine.

In your case, however, either because of a Cargo.lock file already present (which you can force to "update" to the latest version by running cargo update), or because of some other crate in your dependency tree requiring a version of cfg-if < 0.1.6, cargo ends up picking such a version of cfg-if, which causes the compilation error.

The solution is to fork rustyline to fix that line, submit a PR to the repo, and in the meantime, depend on your "fixed" rustyline by using the patch key of a Cargo.toml:

[patch.crates-io]
rustyline = { git = "https://github.com/vsajip/rustyline" }
5 Likes

This is really helpful, thanks!