写Rust,有三大内伤
忽略掉问题,相信程序员的操作。大多数传统的系统编程语言,如C++,都走了这条路。
使所有对象不可变。该选项是Haskell和Clojure中纯函数编程技术的基础。
完全禁止引用。Val语言探索了这种编程风格。
采用防止修改引用对象的类型系统。ATS和Rust等语言选择了这条路。
let x = f("a very long string".to_string());
let y = g("a very long string".to_string());
// …
let s = "a very long string".to_string();
let x = f(s);
let y = g(s);
let s = "a very long string".to_string();
f(s.clone());
g(s);
h(s) 。
let s = "a very long string".to_string();
f(s.clone());
g(s);
// fifty lines of code...
h(s); // ← won’t compile, you need scroll up and update g(s).
// Do we have to type "MyType::from" every time?
// How about introducing an alias?
let x = MyType::from(b"bytes");
let y = MyType::from("string");
// Nope, Rust won't let us.
let f = MyType::from;
let x = f(b"bytes");
let y = f("string");
// - ^^^^^^^^ expected slice `[u8]`, found `str`
// |
// arguments to this function are incorrect
// Compiles fine, but is longer than the original.
fn f<T: Into<MyType>>(t: T) -> MyType { t.into() }
let x = f(b"bytes");
let y = f("string");
impl State {
fn tick(&mut self) {
self.state = match self.state {
Ping(s) => { self.x += 1; Pong(s) }
Pong(s) => { self.x += 1; Ping(s) }
}
}
}
impl State {
fn tick(&mut self) {
self.state = match self.state {
Ping(s) => { self.inc(); Pong(s) } // ← compile error
Pong(s) => { self.inc(); Ping(s) } // ← compile error
}
}
fn inc(&mut self) {
self.x += 1;
}
}
struct S { map: HashMap<i64, String>, def: String }
impl S {
fn ensure_has_entry(&mut self, key: i64) {
// Doesn't compile with Rust 2018:
self.map.entry(key).or_insert_with(|| self.def.clone());
// | ------ -------------- ^^ ---- second borrow occurs...
// | | | |
// | | | immutable borrow occurs here
// | | mutable borrow later used by call
// | mutable borrow occurs here
}
}
struct S { map: HashMap<i64, String>, def: String }
impl S {
fn ensure_has_entry(&mut self, key: i64) {
use std::collections::hash_map::Entry::*;
// This version is more verbose, but it works with Rust 2018.
match self.map.entry(key) {
Occupied(mut e) => e.get_mut(),
Vacant(mut e) => e.insert(self.def.clone()),
};
}
}
struct Hex(Vec<u8>);
impl std::fmt::Display for Hex {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.iter().try_for_each(|b| write!(f, "{:02x}", b))
}
}
println!("{}", Hex((0..32).collect()));
// => 000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
fn complex_function(bytes: &Vec<u8>) {
// … a lot of code …
println!("{}", &Hex(bytes)); // That does not work.
println!("{}", Hex(bytes.clone())); // That works but is slow.
// … a lot of code …
}
/// A WebAssembly global along with its export specification.
/// The lifetime parameter exists to support zero-copy deserialization
/// for the `&str` fields at the leaves of the structure.
/// For a variant with owned types at the leaves, see `OwnedGlobalSpec`.
pub struct GlobalSpec<'a> {
global: Global<'a>,
export_names: Vec<&'a str>,
}
…
/// A variant of `GlobalSpec` with owned strings throughout.
/// This type is useful when directly building up a value to be serialized.
pub struct OwnedGlobalSpec {
global: OwnedGlobal,
export_names: Vec<String>,
}
GlobalSpec<a>是代码作者从字节缓冲区解析的视图对象。此视图的各个字段指向缓冲区的相关区域。此表示对于需要检查GlobalSpec类型的值而不修改它们的函数很有用。
OwnedGlobalSpec是一个包:它不包含对其他数据结构的引用。此表示对于构造GlobalSpec类型的值并将其传递或放入容器的函数很有用。
struct Db { /* … */ }
struct Snapshot<'a> { /* … */ }
impl Db { fn snapshot<'a>(&'a self) -> Snapshot<'a>; }
// There is no way to define the following struct without
// contaminating it with lifetimes.
struct DbSnapshot {
snapshot: Snapshot<'a>, // what should 'a be?
db: Arc<Db>,
}
let x = vec!["a".to_string(), "b".to_string()];
match x {
// - help: consider slicing here: `x[..]`
["a", "b"] => println!("OK"),
// ^^^^^^^^^^ pattern cannot match with input type `Vec<String>`
_ => (),
}
let x = vec!["a".to_string(), "b".to_string()];
match x[..] {
// ----- this expression has type `[String]`
["a", "b"] => println!("OK"),
// ^^^ expected struct `String`, found `&str`
_ => (),
}
let x = vec!["a".to_string(), "b".to_string()];
// We have to allocate new storage.
let x_for_match: Vec<_> = x.iter().map(|s| s.as_str()).collect();
match &x_for_match[..] {
["a", "b"] => println!("OK"), // this compiles
_ => (),
}
Forget about balancing Red-Black trees in five lines of code in Rust
impl Service {
pub fn do_something(&self) {
let guard = self.lock.read();
// …
self.helper_function(); // BUG: will panic or deadlock
// …
}
fn helper_function(&self) {
let guard = self.lock.read();
// …
}
}
Rust在这方面与大多数现代语言相似。然而,它会给你一种虚假的安全感。
由于可变性和生存期注释,状态突变是显式的。
由于普遍存在的Result类型,错误处理是明确和直观的。
如果正确使用,这些功能通常会导致神秘的编译效果。
fn innocently_looking_function() {
tokio::spawn(some_async_func());
// ^
// |
// This code will panic if we remove this line. Spukhafte Fernwirkung!
} // |
// |
fn main() { // v
let _rt = tokio::runtime::Runtime::new().unwrap();
innocently_looking_function();
}
如果运行时是一个显式参数,则除非程序员构造了一个运行时并将其作为参数传递,否则代码不会编译。当运行时是隐式的时,您的代码可能编译得很好,但如果您忘记用神奇的宏注释主函数,则会在运行时崩溃。
混合选择不同运行时的库非常复杂。如果这个问题涉及同一运行时的多个主要版本,那么这个问题就更加令人困惑了。笔者编写异步Rust代码的经验与异步工作组收集的真实情况,可以说是一个悲惨的“事故”!
同步函数可以调用其他同步函数并获得结果。异步函数可以调用和.await其他异步函数以获得结果。
我们不能直接从sync函数调用和等待异步函数。我们需要一个异步运行时,它将为我们执行一个异步函数。
我们可以从异步函数调用同步函数。但要小心!并非所有同步功能都是相同的蓝色。
原文链接:
https://mmapped.blog/posts/15-when-rust-hurts.html#filesystem-shared-resource
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
随时掌握互联网精彩
- 1 奋力打开改革发展新天地 7979969
- 2 男子驾车冲撞小学生被判死缓 7982779
- 3 微信可以线上送实体礼物了 7840928
- 4 “冷资源”里的“热经济” 7725331
- 5 刘诗诗方辟谣离婚 7640470
- 6 全球约有1.9亿妇女为内异症患者 7532109
- 7 遭恶犬袭击4岁男童或需全身换血 7466546
- 8 肖战新片射雕英雄传郭靖造型曝光 7328202
- 9 孙颖莎提车 7291446
- 10 蒋欣生图更是妈妈级别 7134261