26

I am trying to configure an example project in Rust to work.

My structure is:

  • src/potter.rs
  • tests/tests.rs

And my Cargo.toml

[package]
name = "potter"
version = "0.1.0"
authors = ["my name"]
[dependencies]

My potter.rs contains:

pub mod potter {
    pub struct Potter {

    }

    impl Potter  {
        pub fn new() -> Potter {
         return Potter {};
        }
    }

}

And my tests.rs contains:

use potter::Potter;

    #[test]
    fn it_works() {

        let pot = potter::Potter::new();
        assert_eq!(2 + 2, 4);
    }

But I receive this error:

error[E0432]: unresolved import `potter`
 --> tests/tests.rs:1:5
  |
1 | use potter::Potter;
  |     ^^^^^^ Maybe a missing `extern crate potter;`?

error[E0433]: failed to resolve. Use of undeclared type or module `potter`
 --> tests/tests.rs:6:19
  |
6 |         let pot = potter::Potter::new();
  |                   ^^^^^^ Use of undeclared type or module `potter`

warning: unused import: `potter::Potter`
 --> tests/tests.rs:1:5
  |
1 | use potter::Potter;
  |     ^^^^^^^^^^^^^^
  |
  = note: #[warn(unused_imports)] on by default

If I add extern crate potter;, it doesn't fix anything...

error[E0463]: can't find crate for `potter`
 --> tests/tests.rs:1:1
  |
1 | extern crate potter;
  | ^^^^^^^^^^^^^^^^^^^^ can't find crate
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
carlos.baez
  • 1,063
  • 2
  • 11
  • 31

1 Answers1

25

Go back and reread The Rust Programming Language about packages, crates, modules and the filesystem.

Common pain points:

  • Every programming language has its own way of dealing with files — you cannot just assume that because you've used any other language that you will magically get Rust's take on it. That's why you should go back and re-read the book chapter on it.

  • Each file defines a module. Your lib.rs defines a module of the same name as your crate; a mod.rs defines a module of the same name as the directory it's in; every other file defines a module of the name of the file.

  • The root of your library crate must be lib.rs; binary crates may use main.rs.

  • No, you really shouldn't try to do non-idiomatic filesystem organization. There are tricks to do most anything you want; these are terrible ideas unless you are already an advanced Rust user.

  • Idiomatic Rust does not generally place "one type per file" like many other languages. Yes, really. You can have multiple things in one file.

  • Unit tests usually live in the same file as the code it's testing. Sometimes they will be split out into a file containing the submodule, but that's uncommon.

  • Integration tests, examples, benchmarks all have to import the crate like any other user of the crate and can only use the public API.


To fix your issue:

  1. Move your src/potter.rs to src/lib.rs.
  2. Remove pub mod potter from src/lib.rs. Not strictly required, but removes needless nesting of modules.
  3. Add extern crate potter to your integration test tests/tests.rs (Only needed if you are using Rust 2015).

filesystem

├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
├── target
└── tests
    └── tests.rs

src/lib.rs

pub struct Potter {}

impl Potter {
    pub fn new() -> Potter {
       Potter {}
    }
}

tests/tests.rs

use potter::Potter;

#[test]
fn it_works() {
    let pot = Potter::new();
    assert_eq!(2 + 2, 4);
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • I've `tests/common.rs` where I'm doing this: `#[macro_use] extern crate mylib;` but it says *"an `extern crate` **loading macros** must be at the crate root"*. So I'm wondering which file is *"crate root"* for *tests* directory? Is there any analogous to *src/lib.rs*? – Nawaz Oct 07 '18 at 16:39
  • 1
    @Nawaz every single file in the `tests` directory is compiled as a separate binary crate. Each of those is the crate root. See also [Where should I put test utility functions in Rust?](https://stackoverflow.com/q/37993886/155423); [What is an idiomatic way to have shared utility functions for integration tests and benchmarks?](https://stackoverflow.com/q/44539729/155423). – Shepmaster Oct 07 '18 at 16:45
  • So why does `#[macro_use] extern ... ` not work for `tests/common.rs` and it is asking me to put in some `crate root`. What `crate root` is, is not clearly defined it seems. – Nawaz Oct 07 '18 at 16:48
  • 1
    Because you haven't put it at the crate root. "crate root" is a well-defined term - it's the entrypoint of the crate. **Every single file in the tests directory** is compiled as a separate binary crate. That means that your `tests/common.rs` is being compiled as a binary so that's one crate root (probably not what you want, either). Presumably you are also importing that file as a module from another file like `tests/foo.rs` — foo.rs is **also** a crate root. – Shepmaster Oct 07 '18 at 16:53
  • Ohh.. I see. I'm getting that error because of `mod common;` as it is being used as sorta library/utility, not executable! – Nawaz Oct 07 '18 at 16:54
  • 2
    I moved all the content from `tests/common.rs` to `tests/common/mod.rs` and now I'm able to do `mod common;` in other test files. It works. Thanks for the help. – Nawaz Oct 07 '18 at 16:57
  • Also need to make sure that you have the correct linkage. That is, `[lib]` in your `Cargo.toml` with the right settings https://doc.rust-lang.org/reference/linkage.html. I had `crate-type=cdylib`, and it wouldn't work. https://github.com/rust-lang/cargo/issues/6659 – vaughan Oct 01 '21 at 22:21
  • 1
    "Unit tests usually live in the same file as the code it's testing." That's absolutely wild. Do people really find that maintainable? – Matthew Dean Nov 28 '21 at 04:26
  • 1
    @MatthewDean yes. – Shepmaster Nov 28 '21 at 12:50
  • What about using `super::*`, does it help in this case? I am new to rustc – collinsmarra Jul 23 '22 at 17:52
  • Considering that answer, if you want to 100% test coverage, all your code should aside inside lib.rs, that's totally unacceptable! – deathangel908 Jul 23 '23 at 13:38