腐蚀rust服务器命令_Rust 包装和板条箱的使用

70c062bd34d081baf9b8b7ee24017642.png

我们将介绍的模块系统的第一部分是包装和包装箱。条板箱是二进制文件或库。该箱根是源文件是生锈编译器开始,让你的箱子的根模块(我们将在解释深度模块“定义模块来控制范围和隐私”一节)。甲包是一个或多个板条箱,其提供一组功能。一个软件包包含一个Cargo.toml文件,该文件描述了如何构建这些包装箱。

几个规则确定软件包可以包含的内容。一个程序包必须包含零个或一个库条板箱,并且不能包含更多。它可以包含任意数量的二进制板条箱,但必须至少包含一个板条箱(库或二进制)。

让我们逐一介绍创建包时发生的情况。首先,我们输入命令cargo new:

$ cargo new my-project     Created binary (application) `my-project` package$ ls my-projectCargo.tomlsrc$ ls my-project/srcmain.rs

当我们输入命令时,Cargo创建了一个Cargo.toml文件,为我们提供了一个包。在查看Cargo.toml的内容时,没有提到 src / main.rs,因为Cargo遵循一个约定,即src / main.rs是与包同名的二进制包的包根。同样,Cargo知道,如果程序包目录包含src / lib.rs,则程序包包含与程序包同名的库条板箱,并且src / lib.rs是其板条箱根。Cargo传递板条箱根文件rustc以构建库或二进制文件。

在这里,我们有一个仅包含src / main.rs的软件包,这意味着它仅包含一个名为的二进制条板箱my-project。如果软件包包含src / main.rs 和src / lib.rs,则它有两个包装箱:库和二进制文件,两者的名称与软件包相同。通过将文件放在src / bin目录中,一个软件包可以具有多个二进制文件箱:每个文件将是一个单独的二进制文件箱。

板条箱会将范围内的相关功能分组在一起,因此该功能易于在多个项目之间共享。例如,rand我们在第二章中使用的 板条箱提供了生成随机数的功能。通过将rand板条箱纳入我们项目的范围,我们可以在自己的项目中使用该功能。rand板条箱提供的所有功能都可以通过板条箱的名称进行访问rand。

将板条箱的功能保持在其自己的范围内可以阐明是在我们的板条箱中还是在rand板条箱中定义了特定功能,并防止了潜在的冲突。例如,rand板条箱提供了一个名为的特征 Rng。我们还可以在自己的板条箱中定义一个structnamed Rng。由于板条箱的功能是在其自己的作用域中命名的,因此当我们将其添加rand为依赖项时,编译器不会对名称Rng所指的内容感到困惑。在我们的箱子中,它指的struct Rng是我们定义的。我们可以Rng从rand板条箱以方式访问该 特征rand::Rng。

定义模块以控制范围和隐私

在本节中,我们将讨论模块和模块系统的其他部分,即允许您命名项目的路径。use进入范围的关键字;以及将pub项目设为公开的关键字。我们还将讨论as关键字,外部包和glob运算符。现在,让我们专注于模块!

模块使我们可以将板条箱中的代码分为几组,以提高可读性和重用性。模块还控制项目的隐私,即项目是可以由外部代码使用(公共)还是内部实现详细信息,而不是供外部使用(私有)。

作为示例,让我们写一个提供餐厅功能的库箱。我们将定义函数的签名,但将它们的主体留空以专注于代码的组织,而不是实际在代码中实现餐厅。

在餐饮业中,餐厅的某些部分称为 房屋前部,而其他部分称为房屋后部。屋前就是顾客的所在。主机在这里招待客户,服务器在接受订单和付款,调酒师在这里喝酒。屋后是厨师在厨房工作,洗碗碟机,经理进行行政工作的地方。

为了以与实际餐厅相同的方式构造板条箱,我们可以将功能组织到嵌套模块中。restaurant通过运行创建一个新的库 cargo new --lib restaurant;然后将清单7-1中的代码放入src / lib.rs中,以定义一些模块和函数签名。

文件名:src / lib.rs

mod front_of_house {    mod hosting {        fn add_to_waitlist() {}        fn seat_at_table() {}    }    mod serving {        fn take_order() {}        fn serve_order() {}        fn take_payment() {}    }}

清单7-1:front_of_house包含其他模块的模块,这些模块随后包含函数

我们以mod关键字开头定义模块,然后指定模块名称(在本例中为front_of_house),并将大括号放在模块主体周围。在模块内部,我们可以有其他模块,在这种情况下,可以使用hosting和serving。模块还可以保存其他项目的定义,例如结构,枚举,常量,特征或功能(如清单7-1所示)。

通过使用模块,我们可以将相关的定义分组在一起,并命名它们为什么相关。使用此代码的程序员可以更轻松地找到他们想要使用的定义,因为他们可以基于组导航代码,而不必阅读所有定义。向该代码添加新功能的程序员将知道将代码放置在何处,以保持程序的组织性。

之前,我们提到src / main.rs和src / lib.rs称为板条箱根。之所以使用它们,是因为这两个文件中的任何一个的内容都构成了一个模块crate,该模块在板条箱的模块结构的根目录下被命名为模块树。

清单7-2显示了清单7-1中的结构的模块树。

crate └── front_of_house     ├── hosting     │   ├── add_to_waitlist     │   └── seat_at_table     └── serving         ├── take_order         ├── serve_order         └── take_payment

清单7-2:清单7-1中的代码的模块树

此树显示了一些模块如何相互嵌套(例如, hosting嵌套在内部front_of_house)。该树还显示了某些模块彼此为同级,这意味着它们在同一模块中定义(hosting并serving在中定义front_of_house)。为了继续这个家族隐喻,如果模块A包含在模块B中,我们说模块A是模块B的子代,模块B是模块A的父代。请注意,整个模块树都植根于名为的隐式模块下。 crate。

模块树可能会让您想起计算机上文件系统的目录树。这是一个非常恰当的比较!就像文件系统中的目录一样,您可以使用模块来组织代码。就像目录中的文件一样,我们需要一种找到模块的方法。

引用模块树中项目的路径

为了向Rust展示在模块树中哪里找到项目,我们使用路径的方式与浏览文件系统时使用的方式相同。如果要调用函数,则需要知道其路径。

路径可以采用两种形式:

  • 一个绝对路径从一箱根通过使用板条箱名称或文字开始crate。
  • 甲相对路径从当前模块和用途开始self,super或在当前模块中的标识符。

绝对路径和相对路径后均跟有一个或多个用双冒号(::)分隔的标识符。

让我们回到清单7-1中的示例。我们如何调用该 add_to_waitlist函数?这和询问add_to_waitlist函数的路径是什么一样 ?在清单7-3中,我们通过删除一些模块和函数来简化了代码。我们将展示两种add_to_waitlist从eat_at_restaurant板条箱根中定义的新函数调用函数的方法 。该eat_at_restaurant函数是我们的库箱公共API的一部分,因此我们将其标记为pub关键字。在“使用pub关键字公开路径”部分中,我们将详细介绍pub。请注意,该示例尚未编译。我们稍后会解释原因。

文件名:src / lib.rs

mod front_of_house {    mod hosting {        fn add_to_waitlist() {}    }}pub fn eat_at_restaurant() {    // Absolute path    crate::front_of_house::hosting::add_to_waitlist();    // Relative path    front_of_house::hosting::add_to_waitlist();}

清单7-3:add_to_waitlist使用绝对路径和相对路径调用函数

第一次在中调用add_to_waitlist函数时eat_at_restaurant,我们使用绝对路径。该add_to_waitlist函数与定义在同一板条箱中eat_at_restaurant,这意味着我们可以使用crate关键字来启动绝对路径。

之后crate,我们包括每个后续模块,直到我们逐步实现add_to_waitlist。您可以想象一个具有相同结构的文件系统,我们将指定/front_of_house/hosting/add_to_waitlist运行 add_to_waitlist程序的路径。使用crate名称从板条箱根/启动就像使用从外壳中的文件系统根启动一样。

第二次调用add_to_waitlist时eat_at_restaurant,我们使用相对路径。路径以开头front_of_house,在的模块树的同一级别定义的模块名称eat_at_restaurant。这里等效的文件系统将使用path front_of_house/hosting/add_to_waitlist。以名称开头表示路径是相对的。

选择使用相对还是绝对路径是您根据项目决定的。该决定应取决于您是更可能将物料定义代码与使用物料的代码分开移动还是一起使用。例如,如果将front_of_house模块和 eat_at_restaurant函数移动到名为的模块中customer_experience,则需要将绝对路径更新为add_to_waitlist,但是相对路径仍然有效。但是,如果我们将eat_at_restaurant函数分别移动到名为的模块中dining,则add_to_waitlist调用的绝对路径 将保持不变,但是相对路径将需要更新。我们倾向于指定绝对路径,因为它更有可能彼此独立地移动代码定义和项目调用。

让我们尝试编译清单7-3,找出为什么它还不能编译!清单7-4显示了我们得到的错误。

$ cargo build   Compiling restaurant v0.1.0 (file:///projects/restaurant)error[E0603]: module `hosting` is private --> src/lib.rs:9:28  |9 |     crate::front_of_house::hosting::add_to_waitlist();  |                            ^^^^^^^error[E0603]: module `hosting` is private  --> src/lib.rs:12:21   |12 |     front_of_house::hosting::add_to_waitlist();   |                     ^^^^^^^error: aborting due to 2 previous errorsFor more information about this error, try `rustc --explain E0603`.error: could not compile `restaurant`.To learn more, run the command again with --verbose.

清单7-4:编译清单7-3中的代码产生的编译器错误

错误消息指出该模块hosting是私有的。换句话说,我们拥有hosting模块和add_to_waitlist 函数的正确路径,但是Rust不允许我们使用它们,因为它无法访问私有部分。

模块不仅仅对组织代码有用。他们还定义了Rust的 隐私边界:封装实现细节的行不允许外部代码知道,调用或依赖。因此,如果要将项目设为函数或结构私有,则将其放在模块中。

Rust中隐私的工作方式是所有项目(函数,方法,结构,枚举,模块和常量)默认都是私有的。父模块中的项目不能使用子模块中的私有项目,但是子模块中的项目可以使用其祖先模块中的项目。原因是子模块包装和隐藏了它们的实现细节,但是子模块可以看到定义它们的上下文。要继续使用餐厅的隐喻,可以将隐私规则视为餐厅的后台办公室:餐厅客户的隐私是私人的,但是办公室经理可以查看并在其经营的餐厅中做任何事情。

Rust选择以这种方式使模块系统起作用,因此默认隐藏内部实现细节。这样,您就知道可以更改内部代码的哪些部分而不会破坏外部代码。但是,您可以通过使用pub 关键字将项目公开,从而将子模块代码的内部部分公开给外部祖先模块。

使用pub关键字公开路径

让我们回到清单7-4中的错误,告诉我们该hosting模块是私有的。我们希望eat_at_restaurant父模块中的add_to_waitlist函数可以访问子模块中的函数,因此我们hosting用pub关键字标记了 模块,如清单7-5所示。

文件名:src / lib.rs

mod front_of_house {    pub mod hosting {        fn add_to_waitlist() {}    }}pub fn eat_at_restaurant() {    // Absolute path    crate::front_of_house::hosting::add_to_waitlist();    // Relative path    front_of_house::hosting::add_to_waitlist();}

清单7-5:从以下位置声明要使用的hosting模块pubeat_at_restaurant

不幸的是,清单7-5中的代码仍然导致错误,如清单7-6所示。

$ cargo build   Compiling restaurant v0.1.0 (file:///projects/restaurant)error[E0603]: function `add_to_waitlist` is private --> src/lib.rs:9:37  |9 |     crate::front_of_house::hosting::add_to_waitlist();  |                                     ^^^^^^^^^^^^^^^error[E0603]: function `add_to_waitlist` is private  --> src/lib.rs:12:30   |12 |     front_of_house::hosting::add_to_waitlist();   |                              ^^^^^^^^^^^^^^^error: aborting due to 2 previous errorsFor more information about this error, try `rustc --explain E0603`.error: could not compile `restaurant`.To learn more, run the command again with --verbose.

清单7-6:构建清单7-5中的代码产生的编译器错误

发生了什么?在pub关键字前面添加关键字mod hosting可使模块成为公共模块。进行此更改后,如果可以访问front_of_house,就可以访问hosting。但是,内容的hosting仍然是私有的; 公开该模块不会公开其内容。pub模块上的关键字仅允许其祖先模块中的代码引用它。

清单7-6中的错误表明该add_to_waitlist函数是私有的。隐私规则适用于结构,枚举,函数,方法以及模块。

我们还add_to_waitlist通过在pub 关键字的定义之前添加关键字来使该函数公开,如清单7-7所示。

文件名:src / lib.rs

mod front_of_house {    pub mod hosting {        pub fn add_to_waitlist() {}    }}pub fn eat_at_restaurant() {    // Absolute path    crate::front_of_house::hosting::add_to_waitlist();    // Relative path    front_of_house::hosting::add_to_waitlist();}

清单7-7:向 和添加pub关键字mod hosting,fn add_to_waitlist让我们从中调用函数 eat_at_restaurant

现在代码将编译!让我们看一下绝对路径和相对路径,并仔细检查为什么添加pub关键字可以使我们add_to_waitlist根据隐私规则使用这些路径 。

在绝对路径中,我们从crate板条箱模块树的根开始。然后front_of_house在板条箱根中定义模块。该 front_of_house模块是不公开的,但由于eat_at_restaurant 功能是一样的模块定义front_of_house(即, eat_at_restaurant和front_of_house是兄弟),我们可以参照 front_of_house从eat_at_restaurant。接下来是hosting标有的模块pub。我们可以访问的父模块hosting,因此我们可以访问 hosting。最后,该add_to_waitlist函数标有pub,我们可以访问其父模块,因此此函数调用有效!

在相对路径中,逻辑与绝对路径相同(除了第一步):路径不是从板条箱根开始,而是从 front_of_house。该front_of_house模块与定义在同一模块内eat_at_restaurant,因此从eat_at_restaurant定义该模块的模块开始的相对路径 有效。然后,因为hosting和 add_to_waitlist标记为pub,其余的路径都可以使用,并且此函数调用有效!

起始相对路径 super

我们还可以通过super在路径的开头使用来构建从父模块 开始的相对路径。这就像使用..语法来启动文件系统路径。我们为什么要这样做?

考虑清单7-8中的代码,该代码对厨师修理不正确的订单并将其亲自带给客户的情况进行建模。该函数通过指定以以下内容开头的路径来fix_incorrect_order调用函数:serve_orderserve_ordersuper

文件名:src / lib.rs

fn serve_order() {}mod back_of_house {    fn fix_incorrect_order() {        cook_order();        super::serve_order();    }    fn cook_order() {}}

清单7-8:使用从以下开始的相对路径调用函数 super

该fix_incorrect_order功能位于back_of_house模块中,因此我们可以使用super转到的父模块back_of_house,在本例中为crate根。从那里,我们寻找serve_order并找到它。成功!我们认为,如果我们决定重组包装箱的模块树,则back_of_house模块和serve_order功能之间可能会保持相同的关系并在一起移动。因此,我们使用过, super因此如果此代码被移至其他模块,则将来将有更少的地方来更新代码。

公开结构和枚举

我们也可以pub用来将结构体和枚举指定为公共,但是还有一些额外的细节。如果pub在结构定义之前使用,则将结构公开,但结构的字段仍将是私有的。我们可以根据具体情况公开或不公开每个字段。在清单7-9中,我们定义了一个back_of_house::Breakfast具有公共toast字段但私有seasonal_fruit字段的公共结构。这样可以在一家餐馆中为案例建模,顾客可以在这家餐馆选择随餐添加的面包类型,但是厨师会根据季节和库存情况决定随餐搭配哪种水果。可用的水果变化很快,因此客户无法选择水果,甚至看不到会得到哪种水果。

文件名:src / lib.rs

mod back_of_house {    pub struct Breakfast {        pub toast: String,        seasonal_fruit: String,    }    impl Breakfast {        pub fn summer(toast: &str) -> Breakfast {            Breakfast {                toast: String::from(toast),                seasonal_fruit: String::from("peaches"),            }        }    }}pub fn eat_at_restaurant() {    // Order a breakfast in the summer with Rye toast    let mut meal = back_of_house::Breakfast::summer("Rye");    // Change our mind about what bread we'd like    meal.toast = String::from("Wheat");    println!("I'd like {} toast please", meal.toast);    // The next line won't compile if we uncomment it; we're not allowed    // to see or modify the seasonal fruit that comes with the meal    // meal.seasonal_fruit = String::from("blueberries");}

清单7-9:具有一些公共字段和一些私有字段的结构

由于结构中的toast字段back_of_house::Breakfast是公共字段,因此eat_at_restaurant我们可以toast使用点符号对字段进行读写。注意,我们不能在其中使用该seasonal_fruit字段, eat_at_restaurant因为它seasonal_fruit是私有的。尝试取消注释修改seasonal_fruit字段值的行,以查看出现什么错误!

另外,请注意,由于back_of_house::Breakfast具有私有字段,因此该结构需要提供一个公共的关联函数来构造的实例Breakfast(在summer此已将其命名)。如果Breakfast没有这样的功能,我们将无法创建Breakfastin 的实例,eat_at_restaurant因为我们无法在中设置私有seasonal_fruit字段的值 eat_at_restaurant。

相反,如果我们将某个枚举公开,则其所有变体都将公开。我们只需要关键字pubbefore enum,如清单7-10所示。

文件名:src / lib.rs

mod back_of_house {    pub enum Appetizer {        Soup,        Salad,    }}pub fn eat_at_restaurant() {    let order1 = back_of_house::Appetizer::Soup;    let order2 = back_of_house::Appetizer::Salad;}

清单7-10:将枚举指定为公共会使其所有变体成为公共

由于我们Appetizer已将枚举公开,因此可以在中使用Soup和Salad 变体eat_at_restaurant。除非枚举变量是公共变量,否则枚举不是很有用。pub在每种情况下都必须注释所有枚举变量会很烦人 ,因此枚举变量的默认设置是公开的。结构通常在不公开其字段的情况下很有用,因此结构字段遵循一般默认规则,即除非使用注释,否则所有内容均为私有pub。

pub我们还没有涉及另一种情况,那就是我们的最后一个模块系统功能:use关键字。我们将use首先介绍其本身,然后说明如何结合pub和use。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值