6 个版本 (3 个重大更改)
使用旧版 Rust 2015
0.3.1 | 2019年3月30日 |
---|---|
0.3.0 | 2019年3月30日 |
0.2.0 | 2015年11月21日 |
0.1.1 | 2015年9月5日 |
0.0.1 | 2015年5月12日 |
#322 in 并发
每月 26 次下载
31KB
363 行
Rust 的会话类型
这是 Rust 的会话类型实现。
使用此库,您可以实现具有编译时保证,即双方都不会违反通信协议的**双向进程通信**。
入门
session-types 可在 crates.io 上找到。建议您在那里查找最新发布的版本以及指向最新文档构建的链接。
在更新此 README 的最后,最新发布的版本可以使用如下方式
将以下依赖项添加到您的 Cargo 清单...
[dependencies]
session_types = "0.3.1"
...并查看 文档 了解如何使用它。
示例
extern crate session_types;
use session_types::*;
use std::thread;
type Server = Recv<i64, Send<bool, Eps>>;
type Client = <Server as HasDual>::Dual;
fn srv(c: Chan<(), Server>) {
let (c, n) = c.recv();
if n % 2 == 0 {
c.send(true).close()
} else {
c.send(false).close()
}
}
fn cli(c: Chan<(), Client>) {
let n = 42;
let c = c.send(n);
let (c, b) = c.recv();
if b {
println!("{} is even", n);
} else {
println!("{} is odd", n);
}
c.close();
}
fn main() {
let (server_chan, client_chan) = session_channel();
let srv_t = thread::spawn(move || srv(server_chan));
let cli_t = thread::spawn(move || cli(client_chan));
let _ = (srv_t.join(), cli_t.join());
}
我们首先指定一个 协议。协议是由协议类型 Send
、Recv
、Choose
、Offer
、Rec
和 Var
构建的(见 协议类型)。在这种情况下,我们的协议是
type Server = Recv<i64, Send<bool, Eps>>;
它读取
- 接收一个有符号的 64 位整数
- 发送一个布尔值
- 关闭通道
Client
协议是 双,这是会话类型中的一个明确概念。简而言之,这意味着一个进程中的每个协议步骤在另一个进程中都有一个匹配的步骤。如果一个进程想发送一个值,另一个进程必须准备好接收它。 Server
的双是
type Client = Send<i64, Recv<bool, Eps>>;
使用 session-types
,我们不需要手动构建双,因为我们利用特例系统使用 HasDual
特例来模拟此概念。这允许我们这样做
type Client = <Server as HasDual>::Dual;
为什么它很酷?
会话类型不是一个新概念。它们很酷,因为它们允许进行 编译时 验证进程通信。换句话说,我们能够静态地检查两个通信进程是否遵守其共享的通信协议。
但是实现会话类型需要一种方式来模拟协议中某些动作已经发生。在大多数编程语言中,这通常是一个静态操作中的复杂事情,但在Rust中,由于移动语义,这变得很容易。
使用移动语义,我们确保协议中的“前进一步”(发送、接收等)不能重复。
协议类型
任何会话类型的通信协议都是由一些基本构建块构成的。本节逐对介绍它们,展示一个动作及其对偶。
会话类型的通道定义为Chan<E, P>
,其中E
是一个环境,P
是一个协议。环境E
对于新创建的通道始终是()
(即它是空的)。
Eps
这是任何终止协议的最后一步。最简单的例子是
type Close = Eps;
任何类型为Chan<E, Eps>
的通道实现了一个close()
方法,用于关闭连接。Eps
是其自身的对偶,即<Close as HasDual>::Dual = Eps
最简单的通道类型为Chan<(), Eps>
。使用此类通道所能做的唯一事情是关闭连接
let (a, b) = session_channel::<Eps>();
a.close();
b.close();
Send
和Recv
这些是最基本的行为:传输和接收值。类型为T
的Send
的对偶是类型为T
的Recv
。
type S = Send<String, Eps>;
type R = Recv<String, Eps>; // <S as HasDual>::Dual
类型为Chan<E, Send<T, P>>
的通道实现了方法send(T) → Chan<E, P>
。
类型为Chan<E, Recv<T, Q>>
的通道实现了方法recv() → (T, Chan<E, Q>)
。
Choose
和Offer
在协议中存在选择的可能性,其中一个进程可以向另一个进程告知一个决定。Choose
和Offer
结构模拟了此类选择。
type C = Choose<Send<String, Eps>, Recv<String, Eps>>;
type O = Offer<Recv<String, Eps>, Send<String, Eps>>; // <C as Hasdual>::Dual
类型为Chan<E, Choose<P, Q>>
的通道实现两个方法
sel1()→Chan<E, P>
sel2()→Chan<E, Q>
这些方法将协议P
和Q
的选择传达给另一个进程。
类型为 Chan<E, Offer<P, Q>>
的通道实现了 offer() → Branch<Chan<E, P>, Chan<E, Q>>
。其中 Branch
是一个枚举类型,定义为
enum Branch<L, R> {
Left(L),
Right(R),
}
调用 offer()
后,应进行匹配以确定其他进程选择了哪条路径。
match c.offer() {
Branch::Left(c) => …,
Branch::Right(c) => …,
}
Rec
,Var
,S
和 Z
类型 Rec
实现了在协议中进行递归的能力,即提供了一个迭代组件。类型 Rec<P>
允许重复协议 P
。
类型为 Chan<E, Rec<P>>
的通道实现了方法 enter() → Chan<(P, E), P>
。调用 enter()
将协议 P
"存储" 在通道环境中。
然后使用 Var
构造来引用环境中存储的协议。由于环境本质上是一个栈,Var
使用一个表示佩亚诺数(Peano number)的计数器。 Var<Z>
指向栈顶。
类型为 Chan<(P, E), Var<Z>>
的通道实现了方法 zero() → Chan<(P, E), P>
,即 Var<Z>
被栈顶的 P
替换。
type RS = Rec<Send<String, Var<Z>>>;
type RR = Rec<Recv<String, Var<Z>>>; // <RS as HasDual>::Dual
以下程序无限发送一些字符串
let c: Chan<(), Rec<Send<String, Var<Z>>> = …;
let mut c = c.enter();
loop {
c = c.send("Hello!".to_string()).zero();
}
环境中的协议也可以使用 Var<S<N>>
从栈中弹出。这要求栈中至少有一个协议。
类型为 Chan<(P, E), Var<S<N>>>
的通道实现了方法 succ() -> Chan<E, Var<N>>
,该方法从环境和计数器的 S
中移除 P
。
综合以上内容
使用上述构造可以组合更复杂的协议。例如
type Server = Rec<
Offer<
Eps,
Recv<String, Send<usize, Var<Z>>>
>
>;
如下所示
- 在循环中,执行以下操作之一
- 关闭连接
- 或
- 接收一个
String
- 发送回一个
usize
- 返回到开始位置
- 接收一个
示例实现
let c: Chan<(), Server> = …;
let mut c = c.enter(); // c: Chan<(Offer<…>, ()), Offer<…>>
loop {
c = match c.offer() {
Branch::Left(c) => { // c: Chan<(Offer<…>, ()), Eps>
c.close();
break
},
Branch::Right(c) => { // c: Chan<(Offer<…>, ()), Recv<String, Send<usize, Var<Z>>>>
let (c, str) = c.recv(); // c: Chan<(Offer<…>, ()), Send<usize, Var<Z>>>
let c = c.send(str.len()); // c: Chan<(Offer<…>, ()), Var<Z>>
c.zero() // c: Chan<(Offer<…>, ()), Offer<…>>
}
};
}
更多阅读和示例
更多信息,请参阅 Rust 中的会话类型 和示例目录。
依赖项
~395KB