Rust 是一种相当庞大且复杂的编程语言,具有许多特性。但我有一个好消息:不到20% 的功能会给你带来超过 80% 的结果。
以下是我认为在开始 Rust 时必须学习的功能。
枚举
Enums(也叫代数数据类型)当然是新Rustaceans最喜欢的功能,因为它们是Result和Option的基础。
enum Result<T, E> { Ok(T), Err(E), }
pub enum Option<T> { None, Some(T), }
|
枚举允许开发者安全地将其程序的所有可能状态编码到代码中,并在编译时检查他们是否忘记了某个情况。#[derive(Debug, Clone, Copy)] enum Platform { Linux, MacOS, Windows, Unknown, }
impl fmt::Display for Platform { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { Platform::Linux => write!(f, "Linux"), Platform::Macos => write!(f, "macOS"), // Compile time error! We forgot Windows and Unknown } } }
|
异步等待(async-await)
线程被设计用来并行化计算密集型任务。然而,现在很多应用程序(如网络扫描器或网络服务器)都是I/O(输入/输出)密集型的,这意味着如果使用线程,我们的应用程序将花费大量时间来等待网络请求的完成,并使用超过必要的资源。
这些都是async-await所解决的问题,同时提供了一个很好的开发者体验。
Traits
你可能需要切换多个共享相同行为的类似类型的具体实现。
例如,一个存储驱动器。
struct FilesystemStorage { get() // ... put() // ... delete() // ... }
struct S3Storage { get() // ... put() // ... delete() // ... }
|
为此,我们使用traits,在其他语言中也称为接口。
trait Storage { get() // ... put() // ... delete() // ... }
impl Storage for FilesystemStorage { // ... }
impl Storage for S3Storage { // ... }
fn use_storage<S: Storage>(storage: S) { // ... }
|
智能指针
简而言之,它们允许开发者避免生命周期管理,从而写出更干净的代码。它们也是traits obejcts的基础,允许你在运行时选择正确的实现(而不是用泛型编译时)。
struct MyService { db: Arc<DB>, mailer: Arc<dyn drivers::Mailer>, storage: Arc<dyn drivers::Storage>, other_service: Arc<other::Service>, }
|
Collections
Rust的标准库的集合使用Rust编写复杂的算法和商业逻辑变得如此愉快。
let dedup_subdomains: HashSet<String> = subdomains.into_iter().collect();
|
迭代器
迭代器是一个使开发者能够遍历集合的对象。它们可以从标准库的大多数集合中获得。
fn filter() { let v = vec![-1, 2, -3, 4, 5].into_iter();
let _positive_numbers: Vec<i32> = v.filter(|x: &i32| x.is_positive()).collect(); }
|
迭代器是懒惰的:如果它们不被消耗,它们就不会做任何事情。
组合器Combinators
组合器是一个非常有趣的话题。几乎所有你在互联网上找到的定义都会让你的脑袋爆炸,因为它们提出的问题比回答的多。
因此,这里是我的经验性定义。组合器是简化对某种类型T的操作的方法。它们有利于代码的函数性(方法链)风格。
let sum: u64 = vec![1, 2, 3].into_iter().map(|x| x * x).sum();
|
// Convert a `Result` to an `Option` fn result_ok() { let _port: Option<String> = std::env::var("PORT").ok(); }
// Use a default `Result` if `Result` is `Err` fn result_or() { let _port: Result<String, std::env::VarError> = std::env::var("PORT").or(Ok(String::from("8080"))); }
// Use a default value if empty, then apply a function let http_port = std::env::var("PORT") .map_or(Ok(String::from("8080")), |env_val| env_val.parse::<u16>())?;
// Chain a function if `Result` is `Ok` or a different function if `Result` is `Err` let master_key = std::env::var("MASTER_KEY") .map_err(|_| env_not_found("MASTER_KEY")) .map(base64::decode)??;
|
流Stream
流可以被粗略地定义为异步async世界的迭代器iterator。
当你想对同一类型的项目序列进行异步操作时,你应该使用它们,无论是网络套接字、文件,还是长生命的HTTP请求。
任何太大的东西都不能放在内存中,因此应该被分割成小块,或者可能稍后到达,但我们不知道什么时候,或者这只是一个集合(例如Vec或HashMap),我们需要对其应用异步操作。
它们还允许我们轻松地并发执行操作。
async fn compute_job(job: i64) -> i64 { // ... }
#[tokio::main] async fn main() { let jobs = 0..100; let concurrency = 42;
stream::iter(jobs) .for_each_concurrent(concurrency, |job| compute_job(job)).await; }
|
no_std
最后,Rust非常适用于嵌入式开发和shellcodes。因为这些环境不依赖于某个操作系统,所以你一般不能使用Rust的标准库,而是需要使用核心库。对于这些用例,我们使用#![no_std]属性。
#![no_std] #![no_main]
#[panic_handler] fn panic(_: &core::panic::PanicInfo) -> ! { loop {} }
#[no_mangle] fn _start() { // ... }
|