Reputation: 27603
While trying to DRY and reorganize some code in a project, I'm running into issues with scoping and modules.
src/main.rs
:
mod account;
use account::Account;
mod vacancy;
use vacancy::Vacancy;
fn main() {
let key = String::from("s3cr3t");
let uri = String::from("https://localhost:7700");
let account = Account { name: String::from("jdoe") };
account.into_meili(&uri, &key);
let vacancy = Vacancy { title: String::from("CEO") };
vacancy.into_meili(&uri, &key);
}
src/account.rs
:
pub struct Account {
name: String
}
impl IntoMeili for Account {
fn into_meili(&self, uri: &String, key: &String) {
println!("storing document in {} using {}", uri, key);
}
}
src/vacancy.rs
:
pub struct Vacancy {
title: String
}
impl IntoMeili for Vacancy {
fn into_meili(&self, uri: &String, key: &String) {
println!("storing document in {} using {}", uri, key);
}
}
src/into_meili.rs
:
pub trait IntoMeili {
fn into_meili(&self, uri: &String, key: &String);
}
I'm at a loss how to tie these together. I may have gotten some concept entirely wrong, though, so maybe the structure is mistaken in the first place.
When I bring into_meili
into scope in the two modules, I need to tell about the path attribute, which hints that my structure is un-rust-full to begin with:
src/vacancy.rs
:
#[path="into_meili.rs"]
mod into_meili;
use into_meili::IntoMeili;
// ... same as above.
Same in src/account.rs
. This gets rid of the obvious not found in this scope errors for IntoMeili.
This returns the errors to src/main.rs
, in the form of method not found in Vacancy
for account.into_meili()
. The suggestion is to bring the IntoMeili
into scope. With a weird suggestion: use crate::account::into_meili::IntoMeili;
. Hinting that, again, my structure is wrong, as I want to bring crate::into_meili::IntoMeili
in scope instead?
Both suggestions don't work. If I bring use crate::account::into_meili::IntoMeili;
and use crate::vacancy::into_meili::IntoMeili;
it errors with private module
for into_meili
.
When I use mod into_meili; use into_meili::IntoMeili;
I still get method not found in Vacancy
, because, apparently, this is the wrong trait according to this structure.
I probably don't understand a concept in Rust correct; there's a lot to learn in Rust. Obviously the examples are simplified greatly, and because I'm fighting the compiler, above code won't compile. I've added a compiling version in the footnotes, which simply keeps modules out of it entirely.
So, how would I structure my project?
fn into_meili
on objects in the first place, rather than having e.g. a meili::vacancy_into_meili()
etc?Compiling example on rust-playground:
pub trait IntoMeili {
fn into_meili(&self, uri: &String, key: &String);
}
pub struct Account {
name: String
}
impl IntoMeili for Account {
fn into_meili(&self, uri: &String, key: &String) {
println!("storing document in {} using {}", uri, key);
}
}
pub struct Vacancy {
title: String
}
impl IntoMeili for Vacancy {
fn into_meili(&self, uri: &String, key: &String) {
println!("storing document in {} using {}", uri, key);
}
}
fn main() {
let key = String::from("s3cr3t");
let uri = String::from("https://localhost:7700");
let account = Account { name: String::from("jdoe") };
account.into_meili(&uri, &key);
let vacancy = Vacancy { title: String::from("CEO") };
vacancy.into_meili(&uri, &key);
}
Upvotes: 0
Views: 918
Reputation: 60662
Am I structuring the code and modules wrong?
Its mostly that you've not yet understood how files and modules link together. You should never have to use the #[path = ...]
attribute. Your main.rs
should declare that the other files exist via mod
:
mod account; // you already
mod vacancy; // have these
mod into_meili; // add this
Then to access the into_meili
module to implement the trait for Account
and Vacancy
, you can import it via:
use crate::into_meili::IntoMeili;
// or
use super::into_meili::IntoMeili;
See: How do I import from a sibling module?
So your working example is more akin to this:
// into_meili.rs
mod into_meili {
pub trait IntoMeili {
fn into_meili(&self, uri: &String, key: &String);
}
}
// account.rs
mod account {
use super::into_meili::IntoMeili;
pub struct Account {
name: String
}
impl IntoMeili for Account {
fn into_meili(&self, uri: &String, key: &String) {
println!("storing document in {} using {}", uri, key);
}
}
}
// vacancy.rs
mod vacancy {
use super::into_meili::IntoMeili;
pub struct Vacancy {
title: String
}
impl IntoMeili for Vacancy {
fn into_meili(&self, uri: &String, key: &String) {
println!("storing document in {} using {}", uri, key);
}
}
}
// main.rs
use account::Account;
use vacancy::Vacancy;
use into_meili::IntoMeili;
fn main() {
let key = String::from("s3cr3t");
let uri = String::from("https://localhost:7700");
let account = Account { name: String::from("jdoe") };
account.into_meili(&uri, &key);
let vacancy = Vacancy { title: String::from("CEO") };
vacancy.into_meili(&uri, &key);
}
For more explanation I think the blogs Clear explanation of Rust’s module system and Rust modules vs files do a good job of explaining things.
Other than that, your code looks fine to me.
Is it "rust-ish" at all to define methods like
fn into_meili
on objects in the first place, rather than having e.g. ameili::vacancy_into_meili()
etc?
Both have their uses, but you'd use traits if both Account
and Vacancy
are intended to be treated similarly/generically.
Upvotes: 3