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.rs
和 game.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:
- 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.
- 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),
}
}
我对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.rs
和 game.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:
- 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.
- 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),
}
}