The way I have my project files set, I have lib.rs include!() all the other source files, because… it seems confusing otherwise. I recently tried making a trait to extend the byteorder crate’s functionality by implementing read/writes for my structs. No matter what I try though, rustc complains the trait isn’t in scope:

Hm. When I looked at byteorder’s docs, it seemed automatic, which, me being kind of new to Rust, thought it was implicit. Additionally, if I’m supposed to do that, there’s a problem. I tried implementing a trait once before for the f32 type and rustc complained about it not being a type within my crate.

This isn’t an implementation of ReadBytesExt; it’s just a requirement that any type you implement ReadVector3 for must already implement ReadBytesExt.

Laphicet:

When I looked at byteorder’s docs, it seemed automatic

It’s not automatic; it’s just that byteorder implements ReadBytesExt for any type which also implements Read like so:

impl<R: io::Read + ?Sized> ReadBytesExt for R {}

so that when you use byteorder::ReadBytesExt, any types that already implementRead can use the stuff from ReadBytesExt.

Laphicet:

rustc complained about it not being a type within my crate.

When you implement a trait for a type, either the trait or the type (or both) must be in your crate. So you could implement, for example, Read for one of your types, or as dtolnay mentioned, you could implement ReadVector3 for someone else’s types.

I have lib.rs include!() all the other source files, because… it seems confusing otherwise

This may be the way to do it in C, but include! is not the ‘rustic’ way.
There are very sensible reasons to separate your code in multiple files, e.g. programmer readability.
But if you wish to hide this internal organisation from your users, the ‘rustic’ way of doing it is with pub use, which re-exports the functions. Good example in this in the excellent StackOverflow Answer:

second: this Rust-Internals thread discusses (at great length) why f32 doesn’t impl Hash.
tl;dr: It’s complicated because f32 has some edge-cases where equality is fishy, such as NaN != NaN.

The reason why you cannot impl Hash for f32 is due to the “Coherence Rules”.
To prevent conflicting impl’s, you can only impl something if either
A) you are the “boss” of the type being impl'd (e.g. Hash)
B) you are the “boss” of the type being impl'd for (e.g. f32)

Being “the boss” means that you define it in your crate.

These rules ensure that there can only ever be ONE location in the entire Rust world that defines the impl, so you’ll never get errors along the lines of "Bob impls hash for f32, but so do Alice, Joe, Henry and Eve, all are different, which one should I use?"
This is a good thing because your code can never “suddenly” stop compiling if you add a dependency. (e.g. you have Bob’s impl, but later add Alice’s dependency; the Coherence Rules prevent other people breaking your code.)

I tried pub use myself last night but rustc began to complain about stuff not being in scope. This is what I put in place of the include!()s with no luck. Additionally, Cargo complained about being unable to find the byteorder, pod, etc crates I declared extern and had in Cargo.toml. Is extern crate only supposed to go in the main lib file or something? Because the modules that use these crates I’ve done extern crate and stuff like use byteorder::ByteOrder and what not.

pub use bounding_box::{BoundingBox,ReadBoundingBox,WriteBoundingBox};
pub use bounding_sphere::BoundingSphere;
pub use plane::{Plane,PlaneIntersection};
pub use quaternion::Quaternion;
pub use ray::Ray;
pub use vector3::{ReadVector3,Vector3,WriteVector3};

Then this will export all public types from bounding_box as-if they were in your root module. So your crate users don’t know about bounding_box module, and instead they see the types you defined there as-if they were in the root module of the crate. So their code would look like:

extern crate your_crate;
// using your BoundingBox as an example of a concrete type that lives in bounding_box module inside your crate,
// but your crate users don't know that
use your_crate::BoundingBox;

The nice thing about how this works in Rust is you can expose types to your users under a completely different naming/module scheme than what you used internally in your crate. So you can organize the crate to your liking and then export a different scheme that makes sense for users of the crate. For example, this is how the “prelude” modules are usually done, which may have been mentioned upthread.

You can think of pub use as creating symlinks for your crate users - they don’t need to know about your internal module layout if you don’t want to expose that.

I’m trying to make these types public at the root module (lib.rs) level

That’s this part of vitalyd’s reply

vitalyd:

// Make certain types in private modules public
pub use bounding_box::BoundingBox;
// the above makes BoundingBox available to your crate users without exposing bounding_box module itself. So their code would look like:
// extern crate your_crate;
// use your_crate::BoundingBox;

At this point I’d recommend sleeping on it for a night, or going for a long walk. Anything to get some distance from the problem.
I get the impression that you are trying to use too many new ideas at once, so your brain loses the overview. That’s perfectly okay! Rust does a few things differently, and it’s focus on correctness can be very punishing until you get used to it. Module syntax also has some gotcha’s where we can still improve beginner-friendlyness, as Aturon discusses in his post. Vitalyd’s link about “mentally modelling” is also very good in explaining the ‘weird’ logic of pub, use and mod.

Everyone here on these forums has had a similar steep learning curve, so you’re not alone

(p.s. I love how discourse keeps interpreting the filename lib.rs as the (existing!) website https://lib.rs/. We can use backticks (`lib.rs`) to prevent this.)

What would I put in each individual file then? (eg. bounding_box.rs, vector3.rs, etc)? extern crate and use clauses as well?

You don’t need extern crate in them if you’ve already done that in lib.rs. You do need use clauses there to “import” the types from those crates that you’re going to use. Otherwise, bounding_box.rs (and the other modules) just implements all things bounding box.

I did, actually. I double checked too before scratching my head. I did what you said too, but I’m still getting trait not found errors:

error[E0599]: no method named `write_v3` found for type `std::io::Cursor<[{integer}; 24]>` in the current scope
--> src/bounding_box.rs:156:9
|
156 | buf.write_v3::<T>(n.min)?;
| ^^^^^^^^
|
= note: the method `write_v3` exists but the following trait bounds were not satisfied:
`std::io::Cursor<[{integer}; 24]> : std::io::Write`
= help: items from traits can only be used if the trait is implemented and in scope
= note: the following trait defines an item `write_v3`, perhaps you need to implement it:
candidate #1: `vector3::WriteVector3`

Code is basically this and I have the import as use vector3::{ReadVector3,Vector3,WriteVector3};:

= note: the method write_v3 exists but the following trait bounds were not satisfied:std::io::Cursor&lt;[{integer}; 24]&gt; : std::io::Write

It found the trait alright, but your type doesn’t match the constraint. You have impl<T: Write + ?Sized> WriteVector for T. But you apparently have a Cursor<[{integer}; 24]> that you’re trying to call the fn on. This cursor type doesn’t implement Write. It does implement it for Cursor<&mut [u8]> (among other types), which is close to what you’re trying to do apparently.