How do I use #[derive( )] with submodules, file structure?


#1

Hello,

can someone tell me how to introduce derive correctly?
I have a binary, so a main.rs, but I would like to use it in a submodule for my data, the data structure is like this

./main.rs
./data/mod.rs

so in that mod.rs I would like to use serde (serde_derive, serde).
It tells me “an extern crate loading macros must be at the crate root”, if I put
#[macro_use] extern crate serde_derive;
in the mod.rs, so I put it into on top in the main.rs.
But then it cannot find the (Serialize) macro…
#[derive(Serialize,Debug)]
How do I do this?
Thank you


#2

What’s the exact error message?


#3

Crates can only be loaded in the project root, so main.rs or lib.rs.

General good practice is to have sibling main.rs and lib.rs, where main is just the execution logic and lib/ has all your internal logic.

Putting

#[macro_use]
extern crate serde_derive;
extern crate serde

in lib.rs/ will bring #[derive(Serialize)] into scope throughout your library hierarchy.


#4

Yes, I see - I forgot to mention that I have next to main.rs also a lib.rs but which actually only includes or reflects my other submodules … the file structure and the mod, external and use in Rust still looks not too clear to me, but since know I could get along with it, not very satisfied but I think I still need to learn.
So I understood you proposed this now edited unfortunately not working:
lib.rs

#[macro_use] extern crate serde_derive;
extern crate serde;
pub mod ctrl;
pub mod data; // here I want to use #[derive(Serialize)]
pub mod net;

in main.rs I use the modules like


mod ctrl;
mod data;
mod net;

pub use self::data::Collection;
pub use self::ctrl::Ctrl;
pub use self::net::Net;

fn main() {

let init_collection = Collection::new(hostname, &client_id, max_threads);
// so Collection is in data

Some parts of that Collection in data, I would like to serialize

so in ./data/mod.rs
(I also have ./net/mod.rs)
I want to use

#[derive(Serialize)]
struct Audio {
albums: u32,
max_songs: usize,
}

e.g. in that ./data/mod.rs I also have the dependent other crates like

extern crate id3;
extern crate tree_magic; // mime types
extern crate uuid;

on top of that file.

Maybe my whole structure is bad, if it helps it can be found at
[https://github.com/electricherd/audiobookfinder](First rust steps)
where I am doing my Rust steps one step at a time. The fn main() still looks rather nasty …
It looks that I need to re-structure but I am glad to find that problem (to use derive to serialize some data), that makes me possibly switch to a nicer and clearer structure, though I like it, but don’t understand it completely.
How could I use derive in mod data in my structure?


#5

I updated more description: the error is either

cannot find derive macro Serialize in this scope

or

an extern crate loading macros must be at the crate root

depending on where I put

#[macro_use] extern crate serde_derive;
extern crate serde;


#6

Links are [ text ] ( target ) ; you have it switched.


Okay. So, name resolution is a little weird and they’re working on making it less so, but in the meantime:

  • only write extern crate in lib.rs or main.rs; not in any submodules. Doing so causes Weird Things to happen to imported items, ESPECIALLY macros.
  • in main.rs, don’t import modules from your library directly. Do extern crate audiobookfinder; to pull in the library structure and then use pieces from that. This way, lib.rs can handle all the crate imports and you don’t have to redo them in main.rs except for the crates that main.rs directly needs.
//  lib.rs
#[macro_use] extern crate serde_derive;
extern crate serde;
extern crate tree_magic;
extern crate uuid;
extern crate id3;

//  data/mod.rs (can be just data.rs if you're not going to have data/ child mods)
use uuid::Uuid;

#[derive(Serialize, Deserialize)] // works now

//  main.rs
extern crate audiobookfinder as abf;
//  no mod statements
use abf::data;
use abf::data::Collection;
//  etc.

The executable and the library are, technically, separate compilation units within the crate. If you pull in your submodules directly into main.rs, then main.rs has to re-declare all the crate imports from lib.rs.

This is because, despite how it’s popular to dunk on C/C++ #include being a copy/paste mechanism … mod is a copy-paste mechanism. mod file is exactly equivalent to mod file { /* contents of that file */ }

So, the “can’t find Serialize” error was occurring because when src/data/mod.rs got dumped into src/main.rs, main.rs didn’t have Serialize in scope.

In main.rs, extern crate audiobookfinder; pulls the lib/ tree as a foreign, standalone artifact, and doesn’t include the source code directly, it just makes symbols available.


TLDR

  • In this particular case, you can probably get away with killing lib.rs and bringing all the crate imports from that into main.
  • If you have a main/lib split, main should always import lib as a crate, and not redeclare submodules from lib.
  • extern crate should only be done in crate roots, lib.rs or main.rs.

#7

Hi, that’s awesome thanks a bunch, that brought some clarity and good tips.

I could manage to have intermediate compiles that work during rework (with serialization, though I don’t use it yet) but still damn it’s sometimes still a mystery, I am a friend of keeping things separate (decoupled) I am trying sometimes the hard way (like tui/mod.rs instead of tui.rs and ctrl/tui/mod.rs instead of you know what), but during my rework now, it sometimes understands and find things, sometimes not … fighting through it.:grinning:

super::super in use super::super::cursive::Cursive; is actually not so super but yeah, it works. Now I have to find a real bug …thank you