如何在另一个文件中分离结构的默认特征的实现?

How can I separate the implementation of the Default trait for a structure in another file?

我的结构中有大约一百个字段,所以我想将结构定义和数据存储在单独的文件中。

具体来说,我想将定义保存在一个文件中,将 default 实现保存在另一个文件中。我想从 main.rs 访问 default 实现。我不确定如何实现它。

目录树

.
├── Cargo.toml
├── src
│   ├── config.rs
│   ├── lib
│   │   └── config_types.rs
│   └── main.rs

config_types.rs

#[derive(Debug, Copy, Clone)]
pub struct Config {
    a: u8,
    b: u8,
}

impl Config {
    pub fn new(a: u8, b: u8) -> Self {
        Config { a, b }
    }
}

config.rs

#[path = "./lib/config_types.rs"] mod config_types;

use config_types::Config;

impl Default for Config {
    fn default() -> Self {
        Config {
            a: 0b0000_0001,
            b: 0b0000_0000,
        }
    }
}

main.rs

#[path = "./lib/config_types.rs"] mod config_types;
#[path = "./config.rs"] mod config;

use config_types::Config;

fn main() -> ! {
    let config = Config::default();
}

我得到的错误:

error[E0599]: no function or associated item named `default` found for struct `config_types::Config` in the current scope
  --> src/main.rs:64:26
   |
64 |     let config = Config::default();
   |                          ^^^^^^^ function or associated item not found in `config_types::Config`
   | 
  ::: src/./lib/config_types.rs:2:1
   |
2  | pub struct Config {
   | ----------------- function or associated item `default` not found for this
   |
   = help: items from traits can only be used if the trait is implemented and in scope
   = note: the following trait defines an item `default`, perhaps you need to implement it:
           candidate #1: `Default`

如果我将默认实现移至 config_types.rs,错误就会消失。

我怎样才能做到这一点?

  1. 删除 #[path] 属性。相反,使用常规模块并根据需要重新导出。
  2. 在整个 crate 中创建字段 public。

src/main.rs

mod lib {
    pub mod config_types; // Point 1
}
mod config;

use lib::config_types;  // Point 1
use config_types::Config;

fn main() {
    let config = Config::default();
}

src/config.rs

use crate::config_types::Config;  // Point 1

impl Default for Config {
    fn default() -> Self {
        Config {
            a: 0b0000_0001,
            b: 0b0000_0000,
        }
    }
}

src/lib/config_types.rs

#[derive(Debug, Copy, Clone)]
pub struct Config {
    pub (crate) a: u8,  // Point 2
    pub (crate) b: u8,  // Point 2
}

impl Config {
    pub fn new(a: u8, b: u8) -> Self {
        Config { a, b }
    }
}

I have about a hundred fields in the structure

这听起来像是一个自动生成的文件。有关如何解决此问题的其他想法,请参阅构建脚本的 code generation example。如果它不是自动生成的文件,我看不出单独的文件如何使事情变得更容易。

我会避免创建名为 lib 的模块,因为这往往会与常见的 lib.rs 文件混淆。

如果我自己要这样做,我会翻转它,以便将生成的文件包含到手动编辑的文件中:

src/main.rs

mod config;

use config::Config;

fn main() {
    let config = Config::default();
}

src/config.rs

include!("autogenerated.rs");

impl Config {
    pub fn new(a: u8, b: u8) -> Self {
        Config { a, b }
    }
}

impl Default for Config {
    fn default() -> Self {
        Config {
            a: 0b0000_0001,
            b: 0b0000_0000,
        }
    }
}

src/autogenerated.rs

如果生成此文件,它可能不会存在于src目录中!

#[derive(Debug, Copy, Clone)]
pub struct Config {
    pub a: u8,
    pub b: u8,
}

请注意,这些不再需要 public。

mod config_types; 声明了一个 new 模块。即使您通过 #[path = ...] 将它们指向同一个文件,它们也会导致 Config:

的不同声明
  • crate::config_types::Config
  • crate::config::config_types::Config

然后您尝试将 Default 用于一个,而实际上在另一个上定义了它。

所以只能使用其中之一:

  • config.rs 中删除 mod config_types; 并通过 main.rs 导入它,而不是通过 use crate::config_types;
  • main.rs 中删除 mod config_types; 并通过 pub use config_types::Config;
  • config.rs 中导出

很少使用 #[path = ...]。例如, config 的那个是完全不必要的,没有它也可以工作。我建议阅读 Rust Book 的 Managing Growing Projects with Packages, Crates, and Modules 章节,它很好地解释了模块以及如何正确地 import/export 项目。