0

I'm trying to grok the appropriate way to split up my binary project into multiple files. In the Rust book, 7.1 Packages and Crates starts off by including an example of creating a bin project with cargo, but the rest of the examples are lib. I don't really care about the hierarchy of my code since I'm not exposing it in a library. Other, older questions (for example) mention that you generally need <module_name>/mod.rs; while this isn't strictly required, it does seem appropriate for larger library projects.

I have three files:

src/main.rs:

mod A;
mod B;

fn main() {
    let a = A::A { a: 123 };
    let b = B::B { b: 3.14 };

    println!("{:?}", a);
    println!("{:?}", b);
}

src/A.rs:

use crate::B;

#[derive(Debug)]
pub struct A {
    pub a : u32
}

impl From<B::B> for A {
    fn from(b : B::B) -> A {
        A { a: b.b as u32 }
    }
}

src/B.rs:

use crate::A;

#[derive(Debug)]
pub struct B {
    pub b : f32
}

impl From<A::A> for B {
    fn from(a : A::A) -> B {
        B { b: a.a as f32 }
    }
}

This works as expected (snake case warnings removed for brevity):

$ cargo run
A { a: 123 }
B { b: 3.14 }

However, this seems like a bit more song-and-dance than should be necessary: qualifying references with their module name for every reference seems excessive. I would have assumed that I could do something like mod A::A or use A::* to 'import' types, functions, etc.

Is this the expected, idiomatic way to split code up?

Edit:

Per discussion, I updated my source to use use a::*. New code is:

main.rs:

mod a;
mod b;
use a::*; // !
use b::*; // !

fn main() {
    let a = A { a: 123 };
    let b = B { b: 3.14 };

    println!("{:?}", a);
    println!("{:?}", b);
}

a.rs:

use crate::b::*;

#[derive(Debug)]
pub struct A {
    pub a : u32
}

impl From<B> for A {
    fn from(b : B) -> A {
        A { a: b.b as u32 }
    }
}

b.rs:

use crate::a::*;

#[derive(Debug)]
pub struct B {
    pub b : f32
}

impl From<A> for B {
    fn from(a : A) -> B {
        B { b: a.a as f32 }
    }
}

Including both mod a; and use a::*; (changed to follow snake_case convention, but importantly to prevent name conflicts with the previously uppercase A module from conflicting with the still-uppercase A struct) now lets components be referenced without qualification.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
user655321
  • 1,572
  • 2
  • 16
  • 33
  • *is this the expected, idiomatic way to split code up?* — **yes**. – Shepmaster Mar 02 '20 at 20:54
  • *or `use A::*` to 'import' types* — yes, you can. That's the exact syntax. – Shepmaster Mar 02 '20 at 21:20
  • You may wish to re-read [*The Rust Programming Language*](https://doc.rust-lang.org/book/ch07-04-bringing-paths-into-scope-with-the-use-keyword.html), which covers these topics. – Shepmaster Mar 02 '20 at 21:22
  • If I specify `use A::*;` and remove the qualifications (eg, `let a = A { a: 123 };`), I get the error _error[E0422]: cannot find struct, variant or union type `A` in this scope_ – user655321 Mar 02 '20 at 21:23
  • Is this in `B.rs`? Then it's `use crate::A::*;`. `use A::*;` would be what you'd type in `main.rs`. – Shepmaster Mar 02 '20 at 21:24
  • In main.rs, I say `use A::*` and `use B::*`; in a.rs and b.rs, I `use crate::B::*` and `use crate::A::*`, respectively. Qualifications are then removed. I get unresolved imports on A and B and the above failure to find the structs. – user655321 Mar 02 '20 at 21:29
  • 1
    I did not suggest removing the `mod` statements. Again, I highly recommend reviewing *TRPL*. – Shepmaster Mar 02 '20 at 21:43

0 Answers0