1. 初始化项目结构

在序章里,我们已经搭建好了开发需要的环境,在本章中将要初始化项目结构,方便后续开发的时候进行预览。

如果你使用的是WSL进行开发,同时在进行Vulkan设置的时候出了一些问题,导致Vulkan并不能如期运行,因此即使编译没有问题,在接下来运行GUI界面的时候很有可能会出现问题,最好的解决方案就是不使用Vulkan作为渲染后端,而是替换成OpenGL。接下来将会介绍开启的方法。

开启需要的功能集

在序章里,我们引入依赖的时候,只是引入了iced最小化支持,但有些额外的功能需要我们自行开启,本节将会给出一些我们所需要的所有功能集:

# 在 Cargo.toml 内
[dependencies]
- iced = "0.3"
localnative_core = { path = "../localnative/localnative-rs/localnative_core", features =["no_print"] }
+ [dependencies.iced]
+ version = "0.3"
+ default-features = false
+ [features]
+ default = ["preview"]
+ wgpu = [
+ "iced/default",
+ "iced/tokio",
+ "iced/qr_code",
+ ]
+ opengl = [
+ "iced/glow",
+ "iced/tokio",
+ "iced/glow_qr_code",
+ "iced/glow_default_system_font"
+ ]
+ preview = [
+ "wgpu"
+ ]

我们删掉了之前[dependencies]下的iced依赖,转而添加了[dependencies.iced]

除了版本依然是0.3以外,我们还添加了default-features = false这个选项,default-features这个字段设置为false时,将会关闭掉iced默认的features

接着我们构建了自己的features,分别是三个新的featurewgpuopenglpreview。其中默认开启的featurepreview,这个feature将会帮助我们构建预览程序,能够通过添加一些简单的测试用例帮我们熟悉iced的视图。

wgpuopenglfeature想必大家能够看出来是开启了一系列的icedfeature,而preview的内容则是"wgpu",代表的意思是,如果要开启preview这个feature,就会自动开启wgpu这个feature。如果你没有特殊需求,我的建议是此处设置为wgpu就可以。

如果你使用的是WSL,同时遇到了本文开头所说的问题,则此处的preview建议设置为"opengl",它将帮助你解决WSL在进行Vulkan设置时遇到的各种奇怪问题。(因为根本就不适用Vulkan😀)

创建可预览程序集

在接下来的开发过程中,我们会构建各种GUI组件,最后会将这些组件组合成一个完整的应用程序,为了方便教学中进行调试,我们需要构建一个可预览的程序集,因此我们在ln-iced/目录下创建一个叫做previews文件夹:

// before:
.
├── Cargo.lock
├── Cargo.toml
├── src
│   └── lib.rs
└── target
... // generated files
// after:
.
├── Cargo.lock
├── Cargo.toml
++ ├── previews
++ │   └── examample.rs
├── src
++ │   ├── bin.rs
│   └── lib.rs
└── target
... // generated files

我们不仅创建了新的文件夹,同时还新建了两个.rs文件,一个是当前用来作为设置示例的example.rs文件,另一个是我们整个项目的最终可执行文件bin.rs

这也是为啥我们在创建项目之初使用的是cargo new ln-iced --lib的原因,也正因此我们可以通过开启preview这个feature,在不增加最终可执行文件的大小情况下,运行我们想要测试的程序中的其中一部分。

好了,除了文件结构上的改变,我们也需要给Cargo.toml增加一些东西:

# 在 Cargo.toml 内
+ [[bin]]
+ name = "ln"
+ path = "./src/bin.rs"
+ [[bin]]
+ name = "preview-example"
+ path = "./previews/example.rs"
+ required-features = ["preview"]

我们定义了两个bin,这两个同时指定了各自的路径,以及required-features

注意: 能够设置成bin的可执行文件需要在文件内定义main函数。

以及两个文件内的内容:

// ./previews/example.rs
fn main() {
println!("Hello!");
}
// ./src/bin.rs
fn main() {
println!("Hello!");
}

我们通过以下命令来运行我们想要运行的可执行文件:

# 运行名字叫做ln的可执行文件
cargo run --bin ln
# 运行名字叫做preview-example的可执行文件
cargo run --bin preview-example

通过上面两个示例,你一定能够知道是通过--bin这个选项后面跟上我们在Cargo.toml里定义的名字来运行指定的可执行文件的。

如果运行没有什么问题,那么初始化项目结构就算成功了。其中previews这个文件夹,我们后续在开发中需要创建多个可执行文件来帮助我们了解和学习iced。因此创建一个这样的可执行文件的过程在后续教程中会多次出现,在这里已经详细介绍了,所以后续将不会再着重介绍。

最后,在每一章内容完成之后,不要忘了提交一个commit。

课后练习(Quiz)

在本文中我们给一个项目添加了多个可执行文件,相对于可执行文件,在Rust里还有普通的模块,这些模块的添加方式和添加可执行文件的方式有些区别,通常应该如何在一个项目中增加新的模块呢?

A) 创建模块之后,在Cargo.toml文件内添加如下配置:

[[lib]]
name = "new_mod"
path = "./src/new_mod.rs"

即创建了一个新的模块。

B) 在lib.rs后者main.rs内使用mod关键字创建相应的模块文件,同时需要创建相应的Rust文件。 比如:

// ./src/main.rs内
mod new_mod;
use new_mod::hello_from_new_mod;
fn main() {
hello_from_new_mod();
}
// ./src/new_mod.rs内
pub fn hello_from_new_mod() {
println!("hello from new mod!");
}

C) 在Cargo.tom文件内添加如下配置:

[lib]
name = "new_mod"
path = "./src/new_mod.rs"

同时创建选项B中的./src/new_mod.rs文件,调用时只需要这样:

// ./src/main.rs内
// mod new_mod; 不需要这行
use new_mod::hello_from_new_mod;
fn main() {
hello_from_new_mod();
}

答案(Explanation)

B选项和C选项都是正确的做法,其中B选项是通常的做法,在Rust内创建一个新的模块,只需要在lib.rs或者main.rs内使用mod关键字创建模块即可。当然在一些特殊的项目内,我们需要更简洁的项目结构时,则会选择第二种方式了。