7个版本
使用旧的Rust 2015
0.3.0 | 2019年11月20日 |
---|---|
0.2.11 | 2019年8月9日 |
0.2.10 | 2019年4月10日 |
0.2.5 | 2019年1月5日 |
0.1.2 | 2018年11月29日 |
#8 in #continuation
在2个crate中使用了(通过carrier)
21KB
468 行
这是一个死胡同
遗憾的是,在评估rust的愿景与我们的愿景之后,我们决定不再投资rust。osaka是devguard.io的一个重要构建块,但将不会有更新使其可用于公众。
osaka是受go和clay编程语言启发的Rust异步实现
它围绕continuations设计,而不是combinators,这使得代码可读性更高。
为什么
rust的tokio/futures生态系统是一个复杂的单体,对于像我在上面工作的2MB闪存mips盒子这样的受限设备来说效果不佳(tokio本身就有1MB,加上所有futures combinators)。osaka更多的是一种对我有用的hack,而不是试图超越futures.rs。
continuations比combinators更容易理解,并且不需要特定的运行时。
它看起来像什么
最初我计划实现一个proc宏,允许实现golang风格的chans
#[osaka]
pub fn something(bar: chan String) {
let foo <- bar;
}
然而,由于对tokio的替代方案缺乏兴趣,我决定只做最少的努力,所以它看起来像这样
#[osaka]
pub fn something(bar: Channel<String>) {
let foo = sync!(bar);
}
在实际代码中,你可能需要将某个东西注册到一个Poll实例中,以便在poll准备好时重新激活闭包。
#[osaka]
pub fn something(poll: Poll) -> Result<Vec<String>, std::io::Error> {
let sock = mio::UdpSocket::bind(&"0.0.0.0:0".parse().unwrap())?;
let token = poll.register(&sock, mio::Ready::readable(), mio::PollOpt::level()).unwrap();
loop {
let mut buf = vec![0; 1024];
if let Err(e) = sock.recv_from(&mut buf) {
if e.kind() == std::io::ErrorKind::WouldBlock {
yield poll.again(token, Some(Duration::from_secs(1)));
}
}
}
}
pub fn main() {
let poll = osaka::Poll::new();
something(poll).run().unwrap();
}
请注意,在后台没有单例运行时。整个executor(poll)被明确作为参数传递。osaka故意比futures.rs简单。
这里有一些来自osaka-dns的实际代码
#[osaka]
pub fn resolve(poll: Poll, names: Vec<String>) -> Result<Vec<String>, Error> {
//...
let sock = UdpSocket::bind(&"0.0.0.0:0".parse().unwrap()).map_err(|e| Error::Io(e))?;
let token = poll
.register(&sock, mio::Ready::readable(), mio::PollOpt::level())
.unwrap();
//...
// wait for a packet
let pkt = match loop {
// wait for the token to be ready, or timeout
yield poll.again(token.clone(), Some(Duration::from_secs(5)));
if now.elapsed() >= Duration::from_secs(5) {
// timeout
break None;
}
// now the socket _should_ be ready
let (len, from) = match sock.recv_from(&mut buf) {
Ok(v) => v,
Err(e) => {
// but just in case it isn't lets re-loop
if e.kind() == std::io::ErrorKind::WouldBlock {
continue;
}
return Err(Error::Io(e));
}
};
}
// do stuff with the pkt
// ...
}
pub fn test(poll: Poll) -> Result<(), Error> {
let mut a = resolve(
poll.clone(),
vec![
"3.carrier.devguard.io".into(),
],
);
let y = osaka::sync!(a);
println!("resolved: {:?}", y);
Ok(())
}
pub fn main() {
tinylogger::init().ok();
let poll = osaka::Poll::new();
test(poll).run().unwrap();
}
与async/await的区别
最重要的功能之一是所有行为都有明确定义。panic总是osaka中的一个bug,而不是你的代码中的bug。osaka通常更适合“编译它,发货它”的工作流程。并且更倾向于显式性和“容易争论”而不是试图为了“容易编写”的代码而隐藏事件流。
- osaka没有隐式依赖
- osaka::Again包含一个continuation token,而不是隐藏的单例“task”注册表。
- 就绪状态是显式的,这使得代码更容易争论“这里发生了什么”
- 所有错误都是显式的
- 没有未定义的行为。panic是osaka中的bug,而不是你的代码中的bug。
- 如RFC2394中描述的“热函数”在osaka中工作得很好,因为continuation点都是显式的。
依赖项
~3MB
~60K SLoC