Rust:现在重构后结构的可见性 public;无法将 main 添加到 pub(在 crate::main 中)

Rust: Visibility of struct after refactoring now public; cannot add main to pub (in crate::main)

我对rust比较陌生,所以下面也可能是对一个概念的误解: 我从 basic project template 中获取了 ggez 基本项目模板,如下所示:

use ggez::{graphics, Context, ContextBuilder, GameResult};
use ggez::event::{self, EventHandler};

fn main() {
    // Make a Context.
    let (mut ctx, mut event_loop) = ContextBuilder::new("my_game", "Cool Game Author")
        .build()
        .expect("aieee, could not create ggez context!");

    // Create an instance of your event handler.
    // Usually, you should provide it with the Context object to
    // use when setting your game up.
    let mut my_game = MyGame::new(&mut ctx);

    // Run!
    match event::run(&mut ctx, &mut event_loop, &mut my_game) {
        Ok(_) => println!("Exited cleanly."),
        Err(e) => println!("Error occured: {}", e)
    }
}

struct MyGame {
    // Your state here...
}

impl MyGame {
    pub fn new(_ctx: &mut Context) -> MyGame {
        // Load/create resources such as images here.
        MyGame {
            // ...
        }
    }
}

impl EventHandler for MyGame {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
        // Update code here...
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        graphics::clear(ctx, graphics::WHITE);
        // Draw code here...
        graphics::present(ctx)
    }
}

并将其重构为两个文件 main.rsgame.rs 第一个只包含 main() 函数,其他所有内容都进入 game.rs.

main.rs 中的导入更改为

mod game;

use game::MyGame;
use ggez::{graphics, Context, ContextBuilder, GameResult};
use ggez::event::{self, EventHandler};

并将其添加到 game.rs

use ggez::event::EventHandler;
use ggez::{Context, GameResult, graphics};

一切正常只要我做 MyGame public.

然而,现在重构正在发生巨大的变化,因为 MyGame 之前是私有的。 我尝试了几种使用 pub (in infinite_runner::main) 和类似方法的方法,比如 crate::main 但是 none 被接受了各种错误消息。

现在我的问题是,有没有一种方法可以将 MyGame 暴露给 main.rs 而不会暴露给其他任何人?似乎 main 不是一个有效的目标。

我通过反复阅读 Rust 文档设法弄明白了。 虽然回答我自己的问题感觉有点可疑,但实际上可能对其他人有帮助:

TLDR; 我们希望 main 函数中的代码可以访问 MyGame 但拒绝访问外部的任何其他模块。

能不能把代码分解到game.rs: 不行

完全可以做到吗:可以。

原因和方法如下:

来自 Rust 文档 Visibility and Privacy:

With the notion of an item being either public or private, Rust allows item accesses in two cases:

  1. If an item is public, then it can be accessed externally from some module m if you can access all the item's parent modules from m. You can also potentially be able to name the item through re-exports. See below.
  2. If an item is private, it may be accessed by the current module and its descendants.

这意味着我不能同时进行水平分解和水平限制。由于 main.rs 位于顶层,与 public 相同层的任何东西都可以像任何其他模块一样被整个板条箱访问,因为每个模块都可以访问顶层的父级。 因此重构到同层级成一个文件(模块)的答案是:No.

附带说明一下,如果我将代码提取到一个名为 lib.rs 的文件中,那么唯一的区别就是路径,因为顶层的 lib.rs 隐含地只是crate 路径,而在名为 game.rs 的文件中,路径将是 crate::game.

但是可以通过垂直分解来完成与单个文件相同的行为。我们创建一个名为 game 的目录并将 game.rs 移动到该目录中,并将 pub 关键字添加到 MyGame:pub struct MyGame。 类似于 python __init__.py 文件,rust 需要一个文件 mod.rs 来使目录成为一个模块。 在 mod.rs 中你声明了你里面的文件,在我们的例子中是 mod game 。 现在我们可以通过路径 crate::game::game::MyGame 寻址 MyGame,但是由于 game.rs 被声明为私有,所以对 MyGame 的访问是密封的,因为路径的所有元素都必须是 public。 然而,由于 MyGame 被声明为 public,同一级别的任何模块都可以访问它。 我们不能将 main.rs 移动到游戏目录中,但我们可以将其中的代码分解到另一个函数中。由于缺乏幻想,我们称之为 init。我们将 init 函数放在游戏目录中名为 init.rs 的文件中,并在 mod.rs 中将其声明为 public,如下所示:pub mod init。 现在我们可以调用 game::init::init(),因为它是 public,而不是 game::game::MyGame。但是,Init 可以访问 MyGame.

最终结构如下所示:

src
|---main.rs
|---game
    |---mod.rs
    |---game.rs
    |---init.rs

main.rs:

mod game;
use game::init;

fn main() {
    init::init();
}

mod.rs:

pub mod init;
mod game;

game.rs:

use ggez::event::EventHandler;
use ggez::{graphics, Context, GameResult};

pub struct MyGame {
    // Your state here...
}

impl MyGame {
    pub fn new(_ctx: &mut Context) -> MyGame {
        // Load/create resources such as images here.
        MyGame {
            // ...
        }
    }
}

impl EventHandler for MyGame {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
        // Update code here...
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        graphics::clear(ctx, graphics::WHITE);
        // Draw code here...
        graphics::present(ctx)
    }
}

init.rs:

use crate::game::game::MyGame;
use ggez::{ContextBuilder, event};

pub fn init() {
// Make a Context.
    let (mut ctx, mut event_loop) = ContextBuilder::new("my_game", "Cool Game Author")
        .build()
        .expect("aieee, could not create ggez context!");

// Create an instance of your event handler.
// Usually, you should provide it with the Context object to
// use when setting your game up.
    let mut my_game = MyGame::new(&mut ctx);

// Run!
    match event::run(&mut ctx, &mut event_loop, &mut my_game) {
        Ok(_) => println!("Exited cleanly."),
        Err(e) => println!("Error occured: {}", e),
    }
}