Please help me fixing: no `my symbol` in the root

Hi,

I know this has been asked numerous times ... but I really can't fix my code. I decided to have a workspace of separated crates (libraries) plus executables. My folders and files therefore look as below:

/-
 |__numerical
 |    |_Cargo.toml
 |    |_src
 |         |_core
 |         |   |_vec3.rs # pub struct Vec3 {} is inside
 |         |_lib.rs   
 |__src
 |    |_my_app.rs
 |__Cargo.toml

Main Cargo.toml :

[package]
name = "my_app"
edition = "2021"

[workspace]
members = ["numerical"]

[dependencies]
numerical = { path = "./numerical" }

[[bin]]
name = "my_app"
path = "src/my_app.rs"

numerical/Cargo.toml :

[package]
name = "numerical"
edition = "2021"

[lib]
crate-type =["cdylib"]
path = "src/core/vec3.rs"

and finally numerical/src/lib.rs :

mod core;
pub use core::vec3::Vec3;

How in such an arrangement I should import my Vec3 structure in my_app.rs?

Also, I'd glad to read comments how to organize it better. I should note that I'm porting a large project from C++ and I expect hundreds of files. I need a hierarchical structure of modules and sub-modules.

[lib]
crate-type =["cdylib"]
path = "src/core/vec3.rs"

It looks like you are thinking that lib.path is used to include files as part of the library compilation. That is not the case. Since you mention C++ experience, I'll contrast with that:

In cargo+rustc, a single compiler invocation (compiling a single “crate” into a single library or binary) is not given multiple source files (like C compilers accept), but one source file, the “crate root”. That crate root file then effectively includes all other source files, nearly always by means of mod (which improves on C #include by always creating a new module/namespace).

The first step to fixing your problem is to remove path. (Most Cargo.tomls do not need path at all, because they have a file layout which follows Cargo target auto-discovery.) That will cause the compiler to stop reading src/core/vec3.rs and to start reading src/lib.rs (which it is currently ignoring, because it's been told to).

Once you do that, you will see an error like "file not found for core". This is because every mod item has a corresponding file (or inline body) even if its purpose is to contain other modules. Think of modules like functions: functions always have code in them, even if their only job is to call another function. To resolve this error, create a src/core.rs (child modules of the crate root go next to the crate root), with the contents

pub mod vec3;

This is how you tell the compiler that the path core::vec3 should exist, and how you tell the compiler to read the file src/core/vec3.rs. With these two changes, your original src/lib.rs source should now compile successfully, and hopefully so will my_app.rs.

Another thing that is not necessary, but I recommend you do to reduce confusion, is to rename your core module. core is also the name of one of the Rust standard library crates. It is not necessarily a problem but you (or a future reader or maintainer) might end up with a confusing error at some point.


By the way, thanks for including a detailed file listing and contents — the usual problem with helping people learn how to lay out a Rust project is not having all that information. The one thing that would have been good to also include is the full error message from the compiler, with all of its report of the involved file(s) and hints about what to do.

Let us know how it goes after my suggestions! We'll be happy to advise you more on good, as well as working, layout once there's more to see.

4 Likes

Thanks a lot for your help. So here is my progress:

  • I removed lib.path from numerical/Cargo.toml
  • to simplify the layout, I moved vec3.rs one folder up from core to src. This removed the enclosed core submodule and fixed the naming ambiguity issue
    So now in numerical/src folder I have two files: lib.rs and vec3.rs:

lib.rs has just one line:

pub mod vec3;

while vec3.rs defines pub struct Vec3 {}. However, I still can't import Vec3 type in my_app.rs because

error[E0432]: unresolved import `vec3`
 --> src/my_app.rs:5:5
  |
5 | use vec3::Vec3;
  |     ^^^^ use of undeclared crate or module `vec3`

So coming back to my C++ background I'd say the library compiles but is not being linked.

If the vec3 module lives inside the numerical crate and you are trying to use it from the my_app crate, you will need to write use numerical::vec3.

One difference between C++ and Rust is that modules form a tree structure that mirrors the structure on disk, with the crate root (e.g. lib.rs or main.rs) at the top of each tree.

So given the following structure:

  • /
    • first/
      • Cargo.toml
      • src/
        • lib.rs
        • nested_1.rs
    • second/
      • Cargo.toml
      • src/
        • main.rs

If I am in main.rs and want to use some type Foo from nested_1.rs, I would add first to second/Cargo.toml to tell cargo that whenever we build second we also need to build the first crate and link to it. Then you write use first::nested_1::Foo to import the type.

Another key point of difference is that Rust's core compilation unit is the crate, whereas C++ treats each file as separate compilation units.

You also don't need the crate-type ["cdylib"] unless you explicitly want numerical to be compiled to a *.so that can be called from another language (e.g. C). By default, crates are compiled to a rlib (think of this like a Rust-specific object file with metadata for generics and stuff) and you can delete the crate-type key to get this behaviour.

So coming back to my C++ background I'd say the library compiles but is not being linked.

It's linked but you're not referring to it by the proper name.

my_app.rs is the root of a separate crate (= compilation unit), a binary crate (compiled to an executable rather than a library) than numerical/src/lib.rs.

You already have the necessary [dependencies] entry for the library to be available in my_app's namespace, but the path starts with the name of the library crate:

use numerical::vec3::Vec3;

Thanks a lot, now it compiles! To summarize, my mistakes we:

  • incorrect lib.path pointing to the actual source file
  • ["cdylib"] instead of ["rlib"]; the my_app couldn't use numerical library when the compilation type was incorrect
  • use vec3::Vec3 should become use numerical::vec3::Vec3;

There we other issues related to the nested modules; i solved these just by flattening the code structure for now.