rust有哪些坑?
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);