Basic questions/confusion about "use" and "self"

N00b rust programmer here with experience in other languages. I'm working through the diesel "getting started" tutorial (Getting Started with Diesel) and I ran into some confusion around use and self:: references in one of the example binaries they build. Asking here because I think it deals more with rust than with diesel specifically.

The bit of code I ran into is this (src/bin/get_posts.rs: diesel/examples/sqlite/getting_started_step_3/src/bin/get_post.rs at 2.2.x · diesel-rs/diesel · GitHub), with the intro uses for:

use self::models::Post;     // <- problem import line
use diesel::prelude::*;
use diesel_demo::*;     // <- related import line
use std::env::args;

The first thing I noticed was that my editors (both Zed and VSCode+Rust Analyzer) didn't recognize models as being under self (first line), not providing autocomplete for it, and highlighting it as a parse error.

Just to make sure that it wasn't an issue with the editor or a plugin, I tried doing a quick cargo run --bin get_posts which gave a compiler error about an unresolved import for use self::models, with help: a similar path exists: 'diesel_demo::models'.

However, if I moved the use diesel_demo::* line above the use self::models line, then things seemed to parse and run correctly.

So my questions:

  1. Can self:: exist on its own as an import statement, or does it require some kind of crate or module scope declared before it?
  2. Relatedly/broadly, does order of imports matter?
  3. Is the tutorial code incorrect in how it uses self:: for these imports? Should it really be referencing the project crate specifically (diesel_demo) since that's the entity enclosing the models and not the secondary binaries like src/bin/get_posts?

Aside: a file in the same tutorial (src/bin/publish_post.rs) right above get_posts.rs uses similar imports, and was recognized fine by my editors and by the compiler. But when I started working on get_posts.rs I ran into this issue and thus my questions.

Many thanks in advance for helping clarify these for me.

[Update: added question #3]

self in a path refers to the current module. So, use self::models::Post; is valid only if a module models was declared (mod) or imported (use), which isn’t true here.

No.

The order of module declarations (mod) can matter when #[macro_export] is involved, but the order of use does not matter.

It looks like the imports in that particular binary are incorrect, and it should have use diesel_demo_step_3_sqlite::models::Post; instead (referring to the module models of the package’s library crate, not the binary crate).

1 Like

use self::foo::Bar; imports Bar from the foo module that is inside the current module. In order for this foo module to exist and be inside self, either:

  • The current source file must contain a mod foo { /* code for module foo */ } block, OR
  • The current source file must contain a mod foo; statement, and (assuming you're not messing with the path attribute) a foo.rs source file must exist inside a directory with the same name as the current module. The current module can either be structured as a module_name.rs file next to a module_name/ directory or as module_name/mod.rs file; in both cases, foo.rs is directly inside module_name/.

The use self::... line can occur before or after the mod foo block or statement. Import order shouldn't matter, but I don't know how moving the use diesel_demo::* line fixed things in your case.

1 Like

Can self:: exist on its own as an import statement, or does it require some kind of crate or module scope declared before it?

use self;

is not valid, and would be pointless if it were allowed. self is the current module; everything in it is already in scope and needn't be imported, and when you want to use a qualified name (self::Foo), you can do so without a corresponding use.

Relatedly/broadly, does order of imports matter?

In theory, no. More generally, imports only affect name resolution; they do not cause any evaluation that needs to have a well-defined ordering, and naming conflicts are an error, so there's no need for a "first import wins/last import wins" rule. Names are resolved without regard for the order of declarations (including use statements), as well.

Is the tutorial code incorrect in how it uses self:: for these imports? Should it really be referencing the project crate specifically (diesel_demo) since that's the entity enclosing the models and not the secondary binaries like src/bin/get_posts?

I certainly wouldn't write it that way. It's hard to follow what the imports are doing, and comprehensibility is a key part of style as I see it.

Current stable (1.87.0) successfully builds this example, which is what I'd expect. Can you tell us what Rust toolchain version and what IDE you're using that is unable to compile this program?

However, independent of any of that, I would propose changing the imports:

  • Remove the use diesel_demo::* import entirely; and
  • Replace use self::models::Post; with use diesel_demo::models::Post;.

This is equivalent, but, IMO, much easier to follow, especially if you don't have the minutiae of Rust's name-resolution process loaded into your head. @jwodder's answer illustrates why this matters - I know they're a masterful Rust programmer, but I think they misunderstood how use self::models::Post is being resolved in this program.

1 Like

There's a third possibility - use some::other::foo; in the same file, referring to other module.

2 Likes

Thank you so much for this! It helps me understand the scoping better.

It looks like the imports in that particular binary are incorrect, and it should have use diesel_demo_step_3_sqlite::models::Post; instead (referring to the module models of the package’s library crate, not the binary crate).

That was becoming my understanding as I poked around more with the code.

In VSCode+Rust Analyzer, hovering over self in use self::... shows me extern crate get_post, which sorta makes sense (question about this below).

If I include use diesel_demo::* in the imports, when I then hover over models in use self::models::..., VSCode then shows me:

diesel_demo
pub mod models

which almost looks like it's "aliasing" that module from the enclosing lib crate diesel_demo into the src/bin crate for get_post... that is, by importing the enclosing lib crate, it now accepts self::models as a valid import reference/namescope, while mixing the respective owning crates. Am I reading that correctly or how else should I interpret that?

If I don't import the enclosing diesel_demo lib crate, then self::models doesn't resolve and the editor/rust hints at using diesel_demo::models instead.

Related question hinted from above: are the other executables in src/bin not considered to be in enclosing crate scope? My assumption would be that all code at least under src would be part of the "main"/enclosing crate space, but the editor hints seem to show that's not the case?

Each executable is its own crate, and any library code in lib.rs and submodules (if present) is another crate.

1 Like

My bad there... When I was wondering if self:: could exist as its own import or if it needed some other import statement with it, I meant not just use self; but use self::some_scope::*; for example. Thanks for further clarifying self's meaning and scope!

Interesting that it seemed to compile successfully for you off the bat. I'm wondering what might be different with my setup.

I'm using the latest stable (stable-x86_64-unknown-linux-gnu - Up to date : 1.87.0 (17067e9ac 2025-05-09)) via rustup on Arch. I've been using both the latest/recent versions of Zed and VSCode with the latest Rust Analyzer extension.

Just to do a little more digging, I've can replicate the scoping issue I was seeing with use self::models::Post in a barebones lib project from cargo new. Both VSCode and cargo run give me the original error about models not being under self scope and did I mean the diesel_demo::models scope. Let me know if it would help if I posted that mini-project to github or somewhere else. Also, I added some additional info about hover/tooltip hints in my reply to @kpreid above that might play into this.

Thanks much also for the suggestions to help clarify and resolve the imports!

Thank you for clarifying that, @jwodder !

use takes some item(s) from some other module and adds them to the current module. use diesel_demo::*; adds every visible item from diesel_demo, including models, to the current module. So, self::models becomes a valid path because models is now a member of self.

There is no such thing as an “enclosing crate”. Whenever Rust code is being compiled, there is a current crate (nameable as crate::...), and there are previously-compiled crates that can be referred to by their names. These do not nest in any way (except that they can be re-exported with pub use just like other items); their relationships form a dependency graph, not a hierarchy.

3 Likes

When you use something you bring that into the scope of the current module, in your case by doing use diesel_demo::* you'll bring anything that was publicly available in the diesel_demo path, which include the models module. When you then try to access self::models::... the compiler looks for the current module (self) and then a models in its scope (which was brought in by the use diesel_demo::*)

Note: with use diesel_demo::* you are not importing the crate itself, you are importing everything that's inside it into the current module. It's like copying a directory into your desktop vs copying the contents of the directory into your desktop.

No .rs file is generally implicitly available, you need to explicitly declare any module with mod.

In the case of diesel_demo::models, diesel_demo is a crate rooted in the lib.rs file. If you have a lib.rs file in a package named foo then in any file corresponding to the root of a binary target a crate named foo will be available with the contents of the library crate rooted in lib.rs (note that this is considered as an external crate, as if you added foo = ... to your Cargo.toml).

In the lib.rs then models was explicitly declared with mod (and made public for your binary to see wiht pub).

Personally I find this ability to mix a library crate with multiple binary crates in a single Cargo package confusing and I think it's likely not helping you understand how use work. I generally suggest to avoid them and try to stick to the rule to have either a lib.rs or a main.rs and no files under src/bin, then everything else is either declared with a mod or a dependency in the Cargo.toml.

3 Likes

Yeah, I think your points and those from @jwodder and @kpreid are helping me realize how I've tangled concepts of crate, project file hierarchies, and even the cargo.toml together too tightly. From the outside and hitting the ground hard to learn rust, I've made certain assumptions about relationships between those things, and how they affect scoping and importing. This particular tutorial for diesel, by taking a different approach to how it structured its code and dependencies, tossed a spanner in my cogs :slight_smile: I can appreciate that rust provides for such flexibility, but oof, I agree that it adds confusion for novices like me who are trying to get grounded in one area (ORM CRUD) and end up running into unforeseen scoping speedbumps when the tutorial takes a circuitous route in its project setup. That's not so much on rust obviously, but something for tutorial authors to be aware of.

This is only tangentially related, but today I learned:
pub(crate) use my_macro; inside a deeply nested module exports it to the rest of the crate, but only if the use is placed after the macro definition.

foo::bar!();  // works

mod foo {
    macro_rules! bar {
        () => ()
    }

    pub(crate) use bar;    // <-- the trick
}

foo::bar!();  // works

but I don't know how moving the use diesel_demo::* line fixed things in your case.

Wouldn't that mean that after models is loaded you can use self::models in the current module?

Edit: include quote.
Edit2: after reading others' answers I think this is the case.

Thank you all for helping me understand more about how rust does its imports using use and self::.... I greatly appreciate you taking the time to answer my questions.

Follow-up question: Is there a practical reason why someone would do something like this?

use diesel_demo::*;        // lib crate
use self::models::Post;    // this binary src in src/bin

The only thing that might make sense to me is to blanket-import everything from the lib crate, and then use self::models to shorten references to Post. Is this idiomatic in any way? Or are there any other reasons where someone might do this? Just curious.

Many thanks again!

The self:: is entirely superfluous; the second use might as well be written use models::Post;. I would guess that those examples went through a series of mechanical edits to match API changes and there wasn’t enough review of whether the examples made sense afterward.

Personally, I also think that glob imports (::*) should be used very rarely and especially not in examples, since they obscure which names are getting imported from that source. (Yes, this means I’m against so-called “prelude” modules other than the real preludes.) Thus, I personally would write:

use diesel_demo::{
    create_post, establish_connection,
    models::Post,
    schema::posts::dsl::posts,
};

It may not be as concise, but it tells you everything you might want to know about which other files to read to understand the code.

2 Likes

To add a bit context why this import was written this way: This code is there in one or the other form since diesel exists, so for something like 9 years now. Over the years it only saw minor updates to keep in compatible with what's there in diesel. This implies that the code was written before the module system restructuring in the 2018 edition, which makes it very likely that something got either "strange" by just applying rustfix to the code or by literally translating old idoms to the new edition.

That all written: I agree that this can be confusing to new comers and would really appropriate help cleaning up that import (and the others in the example directory). PR's for this are welcome.

2 Likes