Bringing Paths into Scope with the use
Keyword
不得不编写路径来调用函数显得不便且重复。幸运的是,有一种方法可以简化这个过程:我们可以用use
关键字创建一个路径的快捷方式,然后在作用域内的其他地方使用这个较短的名字。
In Listing 7-7, we bring the crate::front_of_house::hosting
module into the scope of the eat_at_restaurant
function so we only have to specify hosting::add_to_waitlist
to call the add_to_waitlist
function in eat_at_restaurant
.
文件名: src/lib.cairo
// section "Defining Modules to Control Scope"
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist(); // ✅ Shorter path
}
Adding use
and a path in a scope is similar to creating a symbolic link in the filesystem. By adding use crate::front_of_house::hosting;
in the crate root, hosting
is now a valid name in that scope, just as though the hosting
module had been defined in the crate root.
Note that use
only creates the shortcut for the particular scope in which the use
occurs. Listing 7-8 moves the eat_at_restaurant
function into a new child module named customer
, which is then a different scope than the use
statement, so the function body won’t compile:
文件名: src/lib.cairo
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting;
mod customer {
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
}
The compiler error shows that the shortcut no longer applies within the customer
module:
$ scarb build
Compiling listing_07_05 v0.1.0 (listings/ch07-managing-cairo-projects-with-packages-crates-and-modules/listing_07_use_and_scope/Scarb.toml)
warn: Unused import: `listing_07_05::hosting`
--> listings/ch07-managing-cairo-projects-with-packages-crates-and-modules/listing_07_use_and_scope/src/lib.cairo:10:28
use crate::front_of_house::hosting;
^*****^
error: Identifier not found.
--> listings/ch07-managing-cairo-projects-with-packages-crates-and-modules/listing_07_use_and_scope/src/lib.cairo:14:9
hosting::add_to_waitlist();
^*****^
error: could not compile `listing_07_05` due to previous error
Creating Idiomatic use
Paths
In Listing 7-7, you might have wondered why we specified use crate::front_of_house::hosting
and then called hosting::add_to_waitlist
in eat_at_restaurant
rather than specifying the use
path all the way out to the add_to_waitlist
function to achieve the same result, as in Listing 7-9.
文件名: src/lib.cairo
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting::add_to_waitlist;
pub fn eat_at_restaurant() {
add_to_waitlist();
}
Although both Listing 7-7 and 7-9 accomplish the same task, Listing 7-7 is the idiomatic way to bring a function into scope with use
. Bringing the function’s parent module into scope with use
means we have to specify the parent module when calling the function. Specifying the parent module when calling the function makes it clear that the function isn’t locally defined while still minimizing repetition of the full path. The code in Listing 7-9 is unclear as to where add_to_waitlist
is defined.
On the other hand, when bringing in structs, enums, traits, and other items with use
, it’s idiomatic to specify the full path. Listing 7-10 shows the idiomatic way to bring the core library’s BitSize
trait into the scope, allowing to call bits
method to retrieve the size in bits of a type.
use core::num::traits::BitSize;
fn main() {
let u8_size: usize = BitSize::<u8>::bits();
println!("A u8 variable has {} bits", u8_size)
}
There’s no strong reason behind this idiom: it’s just the convention that has emerged in the Rust community, and folks have gotten used to reading and writing Rust code this way. As Cairo shares many idioms with Rust, we follow this convention as well.
The exception to this idiom is if we’re bringing two items with the same name into scope with use
statements, because Cairo doesn’t allow that.
Providing New Names with the as
Keyword
There’s another solution to the problem of bringing two types of the same name into the same scope with use
: after the path, we can specify as
and a new local name, or alias, for the type. Listing 7-11 shows how you can rename an import with as
:
文件名: src/lib.cairo
use core::array::ArrayTrait as Arr;
fn main() {
let mut arr = Arr::new(); // ArrayTrait was renamed to Arr
arr.append(1);
}
在这里,我们用别名Arr
将ArrayTrait
带入作用域。现在我们可以用Arr
标识符来访问该trait的方法。
Importing Multiple Items from the Same Module
When you want to import multiple items (like functions, structs or enums) from the same module in Cairo, you can use curly braces {}
to list all of the items that you want to import. This helps to keep your code clean and easy to read by avoiding a long list of individual use
statements.
从同一模块导入多个项的常见语法是:
use module::{item1, item2, item3};
下面是一个从同一个模块导入三个结构体的例子:
// Assuming we have a module called `shapes` with the structures `Square`, `Circle`, and `Triangle`.
mod shapes {
#[derive(Drop)]
pub struct Square {
pub side: u32
}
#[derive(Drop)]
pub struct Circle {
pub radius: u32
}
#[derive(Drop)]
pub struct Triangle {
pub base: u32,
pub height: u32,
}
}
// We can import the structures `Square`, `Circle`, and `Triangle` from the `shapes` module like
// this:
use shapes::{Square, Circle, Triangle};
// Now we can directly use `Square`, `Circle`, and `Triangle` in our code.
fn main() {
let sq = Square { side: 5 };
let cr = Circle { radius: 3 };
let tr = Triangle { base: 5, height: 2 };
// ...
}
Re-exporting Names in Module Files
When we bring a name into scope with the use
keyword, the name available in the new scope can be imported as if it had been defined in that code’s scope. This technique is called re-exporting because we’re bringing an item into scope, but also making that item available for others to bring into their scope, with the pub
keyword.
下面这个例子,让我们重新导出餐厅例子中的add_to_waitlist
函数:
文件名: src/lib.cairo
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
pub use crate::front_of_house::hosting;
fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
Before this change, external code would have to call the add_to_waitlist
function by using the path restaurant::front_of_house::hosting::add_to_waitlist()
. Now that this pub use
has re-exported the hosting
module from the root module, external code can now use the path restaurant::hosting::add_to_waitlist()
instead.
Re-exporting is useful when the internal structure of your code is different from how programmers calling your code would think about the domain. For example, in this restaurant metaphor, the people running the restaurant think about “front of house” and “back of house.” But customers visiting a restaurant probably won’t think about the parts of the restaurant in those terms. With pub use
, we can write our code with one structure but expose a different structure. Doing so makes our library well organized for programmers working on the library and programmers calling the library.
Using External Packages in Cairo with Scarb
You might need to use external packages to leverage the functionality provided by the community. Scarb allows you to use dependencies by cloning packages from their Git repositories. To use an external package in your project with Scarb, simply declare the Git repository URL of the dependency you want to add in a dedicated [dependencies]
section in your Scarb.toml configuration file. Note that the URL might correspond to the main branch, or any specific commit, branch or tag. For this, you will have to pass an extra rev
, branch
, or tag
field, respectively. For example, the following code imports the main branch of alexandria_math crate from alexandria package:
[dependencies]
alexandria_math = { git = "https://github.com/keep-starknet-strange/alexandria.git" }
while the following code imports a specific branch (which is deprecated and should not be used):
[dependencies]
alexandria_math = { git = "https://github.com/keep-starknet-strange/alexandria.git", branch = "cairo-v2.3.0-rc0" }
If you want to import multiple packages in your project, you need to create only one [dependencies]
section and list all the desired packages beneath it. You can also specify development dependencies by declaring a [dev-dependencies]
section.
After that, simply run scarb build
to fetch all external dependencies and compile your package with all the dependencies included.
Note that it is also possible to add dependencies with the scarb add
command, which will automatically edit the Scarb.toml file for you. For development dependencies, just use the scarb add --dev
command.
To remove a dependency, simply remove the corresponding line from your Scarb.toml file, or use the scarb rm
command.