如何在多个模块中使用一个特征

How to use a trait in multiple modules

在尝试 DRY 和重新组织项目中的一些代码时,我 运行 遇到了范围和模块的问题。

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);
}

我不知道如何将这些联系在一起。不过,我可能完全弄错了一些概念,所以可能首先是结构错误。

当我将 into_meili 引入两个模块的范围时,我需要说明 path attribute,这暗示我的结构从一开始就没有锈迹斑斑:

src/vacancy.rs:

#[path="into_meili.rs"]
mod into_meili;
use into_meili::IntoMeili;
// ... same as above.

src/account.rs 相同。这消除了 IntoMeili 的明显 not found in this scope 错误。

此 returns src/main.rs 的错误,形式为 Vacancy 中找不到 account.into_meili() 的方法。建议将 IntoMeili 纳入范围。有一个奇怪的建议:use crate::account::into_meili::IntoMeili;。再次暗示我的结构是错误的,因为我想在范围内引入 crate::into_meili::IntoMeili

这两个建议都不行。如果我带 use crate::account::into_meili::IntoMeili;use crate::vacancy::into_meili::IntoMeili; 它会出错 private module for into_meili.

当我使用 mod into_meili; use into_meili::IntoMeili; 时,我仍然得到 method not found in Vacancy,因为显然,根据这个结构,这是错误的特征。

我可能不理解 Rust 中的某个概念是正确的; Rust 有很多东西要学。显然,这些示例已大大简化,并且因为我与编译器作斗争,所以上面的代码无法编译。我在脚注中添加了一个编译版本,它只是将模块完全排除在外。

那么,我将如何构建我的项目?


编译示例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);
}

Am I structuring the code and modules wrong?

主要是您还不了解文件和模块如何link在一起。您应该永远不会必须使用#[path = ...]属性。您的 main.rs 应该 声明 其他文件存在于 mod:

mod account; // you already
mod vacancy; // have these

mod into_meili; // add this

然后访问into_meili模块来实现AccountVacancy的特征,你可以通过以下方式导入它:

use crate::into_meili::IntoMeili;
// or
use super::into_meili::IntoMeili;

参见:How do I import from a sibling module?

因此您的工作示例更类似于此:

// 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);
}

关于更多解释,我认为博客 Clear explanation of Rust’s module system and Rust modules vs files 解释得很好。


除此之外,我觉得你的代码还不错。

Is it "rust-ish" at all to define methods like fn into_meili on objects in the first place, rather than having e.g. a meili::vacancy_into_meili() etc?

两者都有其用途,但如果 AccountVacancy 都打算被处理 similarly/generically.

,则您将使用特征