首页 > 解决方案 > Rust:重构后结构的可见性现在公开;无法将 main 添加到 pub(在 crate::main 中)

问题描述

我对 rust 比较陌生,所以以下也可能是对一个概念的误解:我从基本项目模板中获取了 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只要我公开,一切正常。

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

现在我的问题是,有没有办法在不暴露MyGamemain.rs其他人的情况下暴露它?似乎 main 不是一个有效的目标。

标签: rustvisibility

解决方案


通过重申 rust 文档,我设法弄清楚了。虽然回答我自己的问题感觉有点可疑,但实际上可能对其他人有帮助:

TLDR;我们希望 main 函数内的代码能够访问 MyGame,但外部的任何其他模块都拒绝访问。

可以通过将代码分解为game.rs:否。

是否可以做到:是的。

以下是原因和方法:

来自 rust 文档Visibility and Privacy

由于项目是公共的或私有的,Rust 允许在两种情况下访问项目:

  1. 如果一个项目是公共的,那么如果您可以从 m 访问该项目的所有父模块,则可以从某个模块 m 外部访问它。您还可以通过再导出来命名该项目。见下文。
  2. 如果一个项目是私有的,它可以被当前模块及其后代访问。

这意味着我不能横向考虑并期望同时横向限制。由于main.rs它位于顶层,因此与任何其他模块一样,整个 crate 都可以访问同一级别的任何公共内容,因为每个模块都可以访问顶层的父级。因此,将同一级别重构为文件(模块)的答案是:否。

附带说明一下,如果我将代码分解到一个名为的文件中,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。现在我们可以通过 path 来处理 MyGame crate::game::game::MyGame,但是由于game.rs声明为私有,因此对 MyGame 的访问是密封的,因为路径的所有元素都必须是公共的。但是,由于 MyGame 被声明为 public,因此同一级别的任何模块都可以访问它。我们无法将其移动main.rs到游戏目录中,但我们可以将其中的代码分解到另一个函数中。让我们称之为init因为缺乏幻想。init.rs我们将 init 函数放在游戏目录中的一个名为的文件中,并在其中声明它为 public,如下所示mod.rspub mod init现在我们可以调用game::init::init(),因为它是公开的,但不是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();
}

模组.rs:

pub mod init;
mod 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)
    }
}

初始化.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),
    }
}

推荐阅读