说Rust有朝一日会取代C成为主流嵌入式编程语言,这未免有点大胆。50多年来,C语言一直主导着这个行业。最接近它的语言是C++,它在所有嵌入式项目中所占的比例也不到25%。
但时代在变化。如果纵观当今联网嵌入式系统的数量,就会发现C语言显然已不再是最合适的工具。C语言语法会让新开发人员感到困惑,并且其行为方式并不能简单的一眼看出,它无法确保安全性或防止常见的内存错误。
嵌入式开发人员,尤其是那些使用联网设备的开发人员,需要一种新工具来完成工作。我想,假以时日,我们会发现Rust就是那个工具。
然而,在以下情况下,学习一门新语言并不容易:
在嵌入式Rust系列文章中,我们将介绍可应用于嵌入式系统的Rust语法、资源和设计方法。
所有人用编程语言编写的第一个应用程序都是“Hello World!”。该程序可帮助开发人员了解语言语法、应用程序启动代码等基本特征,理解编译系统等等。
我们都熟悉C语言的“Hello World!”,它一般看起来这样:
#include
int main() {
printf(“Hello World!”);
return 0;
}
我们引用了包含printf的stdio.h库。main函数返回一个整数,应用程序输出文本“Hello World!”,然后返回一个整数值零。
如果您编写的是C语言版本的应用程序,就必须从头开始编写所有内容。而在Rust中,您可以利用Cargo来开始。Cargo是Rust的包管理器和编译系统,可帮助管理依赖项、编译包、测试和运行Rust项目。
在命令行中,您可以运行以下命令来创建默认的“Hello World”应用程序:
cargo new hello_world
当你执行此操作时,它会生成以下Rust代码:
fn main() {
println!("Hello, world!");
} Embedded Rust
您可以看到我们已经有一个main函数,它是使用fn关键字定义的。在里面,您会发现是使用println!来输出Hello World文本。
在C语言中,我们使用printf来调用函数。println!是一个宏,用于将格式化的文本打印到标准输出,并在后面加上换行符。虽然println!与printf类似,但它提供了Rust语法和类型系统特有的额外安全性和便利性功能。
编译和运行应用程序很简单。只需在终端中输入以下内容即可编译应用程序:
cargo build
然后使用以下命令运行它:
cargo run
您应该会得到类似下面的输出结果:
hello_world % cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/hello_world`
Hello, world!
默认的“Hello World”可以帮助您快速启动和运行项目,但它告诉您有关Rust的工作原理及其语法的信息非常有限。让我们通过进行函数调用并输出结果来使应用程序更有趣一些。
我们将创建一个名为hello()的简单函数,该函数返回对字符串片段“Hello World!”的引用。hello函数的语法如下所示:
fn hello() -> &'static str {
"Hello World!"
}
让我们稍微分解一下,以便您了解此代码的作用。首先,我们使用fn指定hello是一个函数。接下来,我们使用空参数列表()来指定hello不带任何参数。Rust中的参数使用以下符号指定:
name:type
因此,如果您想要有一个名为number且类型为u32的参数,则可以将参数列表定义为
fn hello(number:u32) -> &’static str{
...
}
就像在C语言中一样,您可以使用逗号分隔参数。由于我们不传递任何参数,因此我们将参数列表留空。
函数的返回类型使用->指定。在C语言中,我们在函数前面指定返回类型。在Rust中,我们在函数后面指定。您可以这样理解这种表述:
函数(fn)hello不带任何参数()并返回对整个程序持续期间有效的静态字符串片段的引用(静态)。
现在,如果你是一名C语言程序员,你可能会问字符串片段的引用到底是什么。让我们进一步分析一下。
&符号表示该类型是引用。在本例中,&str是对字符串片段的引用。Rust中的引用允许您借用数据而不占有它的所有权,这有助于管理内存安全。字符串片段是字符串的一个视图。它是对字符串或字符串文字的一部分的引用。
'符号用于引入生命周期符号。生命周期是描述引用有效范围的一种方式。它们通过强制引用的生命周期不超过其指向的数据来防止悬空引用并确保内存安全。
对于我们的示例,我们指定的生命周期是静态生存期。静态生命周期在程序的整个持续时间内有效。通常,字符串文字具有静态生存期,因为它们存储在二进制文件的数据段中。因此,它们在应用程序的整个运行期间都存在。
您可能注意到hello函数的最后一部分是字符串文字“Hello World!”。对于C语言程序员来说,它看起来很奇怪。没有return关键字或分号。
在Rust中,当函数的最后一行是表达式时,首选的惯用方法是省略return和分号!这就是所谓的隐式返回。我们可以将函数写成:
fn hello() -> &'static str {
return "Hello World!";
}
这是完全正确的,但这不是Rust程序员喜欢的编程方式!语法越简单,可读性和可理解性就越强!
为了使我们的应用程序正常运行,我们需要修改main函数。首先,我们可以将其更新如下:
fn main(){
println!("{}", hello());
}
这里要注意的重要语法是,我们使用{}来表示我们想要在字符串中执行替换。它充当放置在字符串中的值的占位符。就像我们在printf中使用%一样。{}的工作原理与Python的str.format()方法非常相似。与C语言不同,Rust的优点在于,Rust可以根据传递给宏的参数自动处理如何显示数据类型。
在我们的示例中,{}将被替换为调用hello函数返回的字符串片段的值。
关于Rust函数的最后一个有趣的点是返回void的概念。在C语言中,我们习惯于看到void作为返回值,但我们在主函数中没有看到它。事实上,我们可以使用以下语法重写主函数:
fn main() -> (){
println!("{}", hello());
}
()类似于C语言中的void。在Rust中,它被称为unit类型。unit类型表示该函数不返回任何有意义的内容。unit类型并不常用,因为它就意味着没有任何有意义的返回,而省略它会使代码更易于阅读和理解。
Rust是一种有趣且丰富的语言,正在嵌入式系统开发中大行其道。虽然你如今可能不会使用它,但你很可能会发现你的下一份工作需要你掌握这种语言!
您可以先在系统上安装Rust工具链,然后尝试我们刚刚讨论过的例子。体验这些例子的时间不会超过30分钟。
(原文刊登于EDN姊妹网站Embedded,参考链接:Embedded Rust: Hello World!,由Ricardo Xie编译。)