[Solved] Why can't I run methods outside their scope?


#1

I’m very new to Rust and I was playing around with the chrono crate when I discovered something I couldn’t understand.

The following code works, and I think it’s written in what most would say the correct way:

extern crate chrono;
use chrono::prelude::*;
fn main() {
  println!("{}", UTC.ymd(2017, 01, 01).and_hms(12, 01, 59));
} // => 2017-01-01 12:01:59 UTC

But if I try NOT to bring chrono into the current scope it doesn’t work:

extern crate chrono;
fn main() {
  println!("{}", chrono::prelude::UTC.ymd(2017, 01, 01).and_hms(12, 01, 59));
} // => no method named `ymd` found for type `chrono::UTC` in the current scope

I imagine a situation where I have two implementations of UTC, chrono::UTC and foo::UTC. Then to use both of them I would need to bring them both into the current scope. But that would result in a conflict.

QUESTIONS:

Why can’t I call the ymd() method outside the chrono scope?
How would I resolve the scenario mentioned above?
Is there any section in the Rust documentation where I can read up on this? I’ve looked but couldn’t find anything.

Sorry in advance if my questions seems dumb and trivial.


#2

chrono::prelude::UTC.ymd() will be accessible in line in your code if you use chrono up above. I also have found this a bit confusing, but I think the general idea is that for each module nothing in the namespace of the external crate is in scope without a use statement. But as long as you have a use foo; anything within foo is accessible if fully specified.


#4

If you look at the docs for UTC and look for ymd, you will see it is part of the TimeZone trait. Trait methods can only be used if the trait is in scope, which is the reason why many creates have a prelude module to help make mass-importing traits easier. (I think it is unfortunate that chrono’s prelude includes so many structs, which IMO kinda defeats the point of a prelude)


(note: this post originally got submitted early and had something leftover from an earlier version where I thought the issue was related to paths. But even if paths aren’t the issue, this thing I linked is still a good read (if a bit dated) if the behavior of use statements feels a bit odd)


#5

Ok, so if I understood you correctly and using my scenario as an example. This would work:

extern crate chrono;
extern crate foo;
use chrono::prelude::*;
use foo::prelude::*;
fn main() {
  println!("{}", chrono::prelude::UTC.ymd(2017, 01, 01).and_hms(12, 01, 59));
  println!("{}", foo::prelude::UTC.ymd(2017, 01, 01).and_hms(12, 01, 59));
}

I thought the two use would make rustc complain that there is a conflict on UTC, since both chrono::UTC and foo::UTC are brought into the current scope.

But if my new understanding is correct then rustc would only complain if I actually tried to call UTC.ymd() instead of chrono::UTC.ymd().

Is this correct?


#6

In the book there is a chapter about modules and crates:
https://doc.rust-lang.org/book/crates-and-modules.html
where also your scenario is explained.


#7

Yes. Here is the current compiler output:


Ambiguous import which is never used

#![allow(unused_imports, unused_variables)] // silence some warnings for science

extern crate a;
extern crate b;
use a::*; // includes a struct X
use b::*; // includes a struct X

fn main() { }

Output:

$ cargo run
   Compiling c v0.1.0 (file:///home/lampam/cpp/throwaway/c)
    Finished debug [unoptimized + debuginfo] target(s) in 0.41 secs
     Running `target/debug/c`

Ambiguous import which is used:

(note: everything prior to main is still present)

fn main() {
    let x = X;
}

Output:

$ cargo run
   Compiling c v0.1.0 (file:///home/lampam/cpp/throwaway/c)
error: `X` is ambiguous
 --> src/main.rs:9:13
  |
9 |     let x = X;
  |             ^
  |
note: `X` could resolve to the name imported here
 --> src/main.rs:5:5
  |
5 | use a::*; // includes a struct X
  |     ^^^^^
note: `X` could also resolve to the name imported here
 --> src/main.rs:6:5
  |
6 | use b::*; // includes a struct X
  |     ^^^^^
  = note: consider adding an explicit import of `X` to disambiguate

error: aborting due to previous error

error: Could not compile `c`.

To learn more, run the command again with --verbose.

Using a qualified path:

(note: everything prior to main is still present. In this case the use statements are not strictly necessary, but they will be in your case since you’re attempting to call a trait method)

fn main() {
    let x = a::X;
}

Output:

$ cargo run
   Compiling c v0.1.0 (file:///home/lampam/cpp/throwaway/c)
    Finished debug [unoptimized + debuginfo] target(s) in 0.46 secs
     Running `target/debug/c`

#8

Thank you! Then I have no more questions.


#9

I read that chapter but I thought it didn’t apply since it calls methods with foo::bar(). In my example with chrono I call methods with foo.bar() (see :: compared to .).