使用 use 关键字将路径引入作用域

不得不编写路径来调用函数显得不便且重复。幸运的是,有一种方法可以简化这个过程:我们可以用use关键字创建一个路径的快捷方式,然后在作用域内的其他地方使用这个较短的名字。

在示例7-5中,我们把restaurant::front_of_house::hosting模块带入到作用域内,所以我们只需要指定 hosting::add_to_waitlist 来调用eat_at_restaurant中的add_to_waitlist 函数。

文件名: src/lib.cairo

#![allow(unused)]
fn main() {
// Assuming "front_of_house" module is contained in a crate called "restaurant", as mentioned in the section "Defining Modules to Control Scope"
// If the path is created in the same crate, "restaurant" is optional in the use statement

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}
    }
}

use restaurant::front_of_house::hosting;

fn eat_at_restaurant() {
    hosting::add_to_waitlist(); // ✅ Shorter path
}
}

示例 7-5: 用 "使用 "将一个模块带入范围。 使用

在作用域中添加 use 和路径类似于在文件系统中创建一个软连接(符号连接,symbolic link)。通过在 crate 根中添加 use restaurant::front_of_house::hosting,hosting 现在是该作用域中的一个有效名称,就像在 crate 根中定义了hosting模块一样。

注意 use 只能创建 use 所在的特定作用域内的短路径。示例 7-6 将 eat_at_restaurant 函数移到一个新的子模块中,这又是一个不同于 use 语句的作用域,所以函数体不能编译:

文件名: src/lib.cairo

#![allow(unused)]
fn main() {
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}
    }
}

use restaurant::front_of_house::hosting;

mod customer {
    fn eat_at_restaurant() {
        hosting::add_to_waitlist();
    }
}
}

示例7-6:一个 use语句只适用于它所在的作用域

编译器错误显示短路径不在适用于 customer 模块中:

❯ scarb build
error: Identifier not found.
 --> lib.cairo:11:9
        hosting::add_to_waitlist();
        ^*****^

创建惯用的 use 路径

在示例6-5中,你可能想知道为什么我们指定restaurant::front_of_house::hosting,然后调用eat_at_restaurant中的hosting::add_to_waitlist,而不是通过指定一直到 add_to_waitlist函数的 use 路径来得到相同的结果,如示例7-7。

文件名: src/lib.cairo

#![allow(unused)]
fn main() {
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}
    }
}

use restaurant::front_of_house::hosting::add_to_waitlist;

fn eat_at_restaurant() {
    add_to_waitlist();
}
}

示例7-7:使用 useadd_to_waitlist 函数引入作用域,这并不符合习惯

尽管示例7-5和7-7都完成了相同的任务,但示例 7-5 是使用 use 将函数引入作用域的习惯用法。要想使用 use 将函数的父模块引入作用域,我们必须在调用函数时指定父模块,这样可以清晰地表明函数不是在本地定义的,同时使完整路径的重复度最小化。示例 7-7 中的代码不清楚 add_to_waitlist 是在哪里被定义的。

另一方面,使用 use 引入结构体、枚举和其他项时,习惯是指定它们的完整路径。示例 7-8 展示了将核心库的 ArrayTrait trait带入作用域。

fn main() {
    let mut arr = ArrayTrait::new();
    arr.append(1);
}

示例7-8:将ArrayTrait引入作用域的习惯用法式

这种习惯用法背后没有什么硬性要求:它只是一种惯例,人们已经习惯了以这种方式阅读和编写 Rust 代码。它只是在Rust社区中出现的惯例。 由于Cairo与Rust共享许多惯例,我们也遵循这一惯例。

这个习惯用法有一个例外,那就是我们想使用 use 语句将两个具有相同名称的项带入作用域,因为Cairo不允许这样做。

使用 as 关键字提供新的名称

使用 use 将两个同名类型引入同一作用域这个问题还有另一个解决办法:在这个类型的路径后面,我们使用 as 指定一个新的本地名称或者别名( alias )。示例7-9显示了如何用as重命名一个导入:

文件名: src/lib.cairo

use array::ArrayTrait as Arr;

fn main() {
    let mut arr = Arr::new(); // ArrayTrait was renamed to Arr
    arr.append(1);
}

示例 7-9:使用 as 关键字重命名引入作用域的类型

在这里,我们用别名ArrArrayTrait带入作用域。现在我们可以用Arr标识符来访问该trait的方法。

从同一模块中导入多个项

当你想从同一个模块中导入多个项(如函数、结构体或枚举)时, 你可以使用大括号{}来列出所有你想导入的项目。 避免了一长串单独的use有助于保持你的代码整洁和便于阅读。

从同一模块导入多个项的常见语法是:

#![allow(unused)]
fn main() {
use module::{item1, item2, item3};
}

下面是一个从同一个模块导入三个结构体的例子:

// Assuming we have a module called `shapes` with the structures `Square`, `Circle`, and `Triangle`.
mod shapes {
    #[derive(Drop)]
    struct Square {
        side: u32
    }

    #[derive(Drop)]
    struct Circle {
        radius: u32
    }

    #[derive(Drop)]
    struct Triangle {
        base: u32,
        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 };
// ...
}

示例 7-10:从同一模块导入多个项

在模块文件中重导出名称

当我们用use关键字将一个名字带入作用域时,在新的作用域中也能够正常使用这个名称,就好像它本来就在当前作用域一样。 这种技术被称为 重导出( re-exporting ),因为我们将一个项目带入作用域、但同时也使这个项目可以被其他人带入他们的作用域。

下面这个例子,让我们重新导出餐厅例子中的add_to_waitlist函数:

文件名: src/lib.cairo

#![allow(unused)]
fn main() {
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}
    }
}

use restaurant::front_of_house::hosting;

fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}
}

示例 7-11: 通过 pub use 使名称可在新作用域中被导入至任何代码

在这个修改之前,外部代码需要使用路径 restaurant::front_of_house::hosting::add_to_waitlist() 来调用 add_to_waitlist 函数。 现在这个 use 从根模块重导出了 hosting 模块,外部代码现在可以使用路径 restaurant::hosting::add_to_waitlist()

当你代码的内部结构与调用你代码的程序员所想象的结构不同时,重导出会很有用。 例如,在这个餐馆的比喻中,经营餐馆的人会想到“前台”和“后台”。但顾客在光顾一家餐馆时,可能不会以这些术语来考虑餐馆的各个部分。 使用 use,我们可以使用一种结构编写代码,却将不同的结构形式暴露出来。这样做使我们的库井井有条,也使开发这个库的程序员和调用这个库的程序员都更加方便。

在Cairo使用外部包与Scarb

你可能需要使用外部包来利用社区提供的功能。要在你的项目中使用Scarb的外部包,请遵循以下步骤:

依赖关系系统仍然是一项正在进行的工作。你可以查看官方的文档

Last change: 2023-09-20, commit: cbb0049