F001 已經提到unsafe和借用所有權天生帶來的問題。除此之外目前還遇到另外幾個讓人不爽的地方,大部分已有RFC提出解決方案,但前途不樂觀。
1. Rust沒有subtype, enum 作為首選型別建模工具又沒有實現Dependent Type,最後要enum套struct,比較醜陋。
摘自rustc原始碼
enum TypeStructure {
Bool, // bool
Reference(Region, Mutability, Type), // &"x T, &"x mut T
Struct(DefId, &"tcx Substs), // Foo<..>
Enum(DefId, &"tcx Substs), // Foo<..>
BareFn(&"tcx BareFnData), // fn(..)
...
}
vs C++
typedef TypeStructure *Ty;
class TypeStructure { .. };
class Bool : public TypeStructure { .. };
class Reference : public TypeStructure { .. };
class Struct : public TypeStructure { .. };
class Enum : public TypeStructure { .. };
class BareFn : public TypeStructure { .. };
2. 常數不能作為泛型引數。所以當結構包含不定長陣列時,要將這個陣列型別作為一個泛型引數,或傳入陣列引用,破壞了封裝。
let mut vector: ArrayDeque = ArrayDeque::new();
vs
let mut vector: ArrayDeque<8> = ArrayDeque::new();
3. 不能棧上分配不定長記憶體。Rust中涉及閉包、trait object、環形引用的部分強制應用堆分配。在部分實時系統裡不能用堆時就比較痛苦了。
4. 錯誤處理上偷師Haskell用Option又沒學完,既沒有monad 也沒有 do notation。
這裡舉future的例子,option和result同理
Rust的Future大概是這樣的。
fn main() {
let mut core = Core::new().unwrap();
let addr = "www.rust-lang.org:443".to_socket_addrs().unwrap().next().unwrap();
let socket = TcpStream::connect(&addr, &core.handle());
let cx = TlsConnector::builder().unwrap().build().unwrap();
let response = socket.and_then(|socket| {
cx.connect_async("www.rust-lang.org", socket).map_err(|e| {
io::Error::new(io::ErrorKind::Other, e)
})
}).and_then(|socket| {
tokio_core::io::write_all(socket, "\
GET / HTTP/1.0\r\n\
Host: www.rust-lang.org\r\n\
\r\n\
".as_bytes())
}).and_then(|(socket, _)| {
tokio_core::io::read_to_end(socket, Vec::new())
});
let (_, data) = core.run(response).unwrap();
別人家Scala的Future。
val usdQuote = Future { connection.getCurrentValue(USD) }
val chfQuote = Future { connection.getCurrentValue(CHF) }
val purchase = for {
usd
chf
if isProfitable(usd, chf)
} yield connection.buy(amount, chf)
purchase onSuccess {
case _ => println("Purchased " + amount + " CHF")
5. 不支援閉包引數佔位符,也沒有部分應用函式。combinator寫起來醜陋不說,還比較容易混淆。比如官方文件說,
let letter_count = ["apple", "banana"].iter().map(|str| { str.len() }).sum();
可以改寫為
let letter_count = ["apple", "banana"].iter().map(str::len).sum();
但當你豁然貫通之際想要重寫下面這段程式碼的時候
let at_least_10: Vec<_> = [8, 9, 10, 11].iter().map(|x| { cmp::min(x, 10) }).collect();
發現Rust其實並沒有部分應用函式。。。
let at_least_10: Vec<_> = [8, 9, 10, 11].iter().map(cmp::min(_, 10)).collect(); // 不能編譯
再看看隔壁scala的
val at_least_10 = List(8, 9, 10, 11).map(_ min 10).reduceLeft(_ + _);
6. Trait 沒有欄位。這個見仁見智,trait預設實現用到結構的欄位時,目前的標準寫法是,trait定義一個 get_xxx()方法給結構實現,不是那麼的高效。我覺得這個feature還是比較重要的,不知為何不在1.0前加入。,core team 早已有人提出, 但也是一直被推遲。
7. 最後是一個生命週期設計上的問題——生命週期過早開始和過早結束,在3年前被核心成員發現並提出解決方案,但一直推遲到1.0釋出直到今天還沒解決。
比方說,下面這段不能編譯
let vec = vec![...];
vec.remove(vec.len() - 1); // vec已被借用
原因是第二句被展開為
Vec::remove(&mut vec, vec.len() - 1); // 第一個引數傳遞可變借用,第二個引數時就不能再借出了
一定要寫成
let len = vec.len();
vec.remove(len - 1);
F001 已經提到unsafe和借用所有權天生帶來的問題。除此之外目前還遇到另外幾個讓人不爽的地方,大部分已有RFC提出解決方案,但前途不樂觀。
1. Rust沒有subtype, enum 作為首選型別建模工具又沒有實現Dependent Type,最後要enum套struct,比較醜陋。
摘自rustc原始碼
enum TypeStructure {
Bool, // bool
Reference(Region, Mutability, Type), // &"x T, &"x mut T
Struct(DefId, &"tcx Substs), // Foo<..>
Enum(DefId, &"tcx Substs), // Foo<..>
BareFn(&"tcx BareFnData), // fn(..)
...
}
vs C++
typedef TypeStructure *Ty;
class TypeStructure { .. };
class Bool : public TypeStructure { .. };
class Reference : public TypeStructure { .. };
class Struct : public TypeStructure { .. };
class Enum : public TypeStructure { .. };
class BareFn : public TypeStructure { .. };
2. 常數不能作為泛型引數。所以當結構包含不定長陣列時,要將這個陣列型別作為一個泛型引數,或傳入陣列引用,破壞了封裝。
let mut vector: ArrayDeque = ArrayDeque::new();
vs
let mut vector: ArrayDeque<8> = ArrayDeque::new();
3. 不能棧上分配不定長記憶體。Rust中涉及閉包、trait object、環形引用的部分強制應用堆分配。在部分實時系統裡不能用堆時就比較痛苦了。
4. 錯誤處理上偷師Haskell用Option又沒學完,既沒有monad 也沒有 do notation。
這裡舉future的例子,option和result同理
Rust的Future大概是這樣的。
fn main() {
let mut core = Core::new().unwrap();
let addr = "www.rust-lang.org:443".to_socket_addrs().unwrap().next().unwrap();
let socket = TcpStream::connect(&addr, &core.handle());
let cx = TlsConnector::builder().unwrap().build().unwrap();
let response = socket.and_then(|socket| {
cx.connect_async("www.rust-lang.org", socket).map_err(|e| {
io::Error::new(io::ErrorKind::Other, e)
})
}).and_then(|socket| {
tokio_core::io::write_all(socket, "\
GET / HTTP/1.0\r\n\
Host: www.rust-lang.org\r\n\
\r\n\
".as_bytes())
}).and_then(|(socket, _)| {
tokio_core::io::read_to_end(socket, Vec::new())
});
let (_, data) = core.run(response).unwrap();
}
別人家Scala的Future。
val usdQuote = Future { connection.getCurrentValue(USD) }
val chfQuote = Future { connection.getCurrentValue(CHF) }
val purchase = for {
usd
chf
if isProfitable(usd, chf)
} yield connection.buy(amount, chf)
purchase onSuccess {
case _ => println("Purchased " + amount + " CHF")
}
5. 不支援閉包引數佔位符,也沒有部分應用函式。combinator寫起來醜陋不說,還比較容易混淆。比如官方文件說,
let letter_count = ["apple", "banana"].iter().map(|str| { str.len() }).sum();
可以改寫為
let letter_count = ["apple", "banana"].iter().map(str::len).sum();
但當你豁然貫通之際想要重寫下面這段程式碼的時候
let at_least_10: Vec<_> = [8, 9, 10, 11].iter().map(|x| { cmp::min(x, 10) }).collect();
發現Rust其實並沒有部分應用函式。。。
let at_least_10: Vec<_> = [8, 9, 10, 11].iter().map(cmp::min(_, 10)).collect(); // 不能編譯
再看看隔壁scala的
val at_least_10 = List(8, 9, 10, 11).map(_ min 10).reduceLeft(_ + _);
6. Trait 沒有欄位。這個見仁見智,trait預設實現用到結構的欄位時,目前的標準寫法是,trait定義一個 get_xxx()方法給結構實現,不是那麼的高效。我覺得這個feature還是比較重要的,不知為何不在1.0前加入。,core team 早已有人提出, 但也是一直被推遲。
7. 最後是一個生命週期設計上的問題——生命週期過早開始和過早結束,在3年前被核心成員發現並提出解決方案,但一直推遲到1.0釋出直到今天還沒解決。
比方說,下面這段不能編譯
let vec = vec![...];
vec.remove(vec.len() - 1); // vec已被借用
原因是第二句被展開為
Vec::remove(&mut vec, vec.len() - 1); // 第一個引數傳遞可變借用,第二個引數時就不能再借出了
一定要寫成
let vec = vec![...];
let len = vec.len();
vec.remove(len - 1);