我想制作一个类似 python 用法的上下文管理器。
我有一个套接字,我可以从中读取和写入。然后,一个设备结构体,它保存套接字,通过套接字命令设备
pub struct MyDevice{
socket: MySocket,
/* ... */
}
impl MyDevice{
pub fn new()->MyDevice { todo!() }
pub fn command(&mut self, s:&str)->std::io::Result<String>{
self.socket.write(s)?;
self.socket.read()
}
/* ... */
}
Run Code Online (Sandbox Code Playgroud)
正常的操作看起来像这样
let mut device = MyDevice::new();
device.command("Push setting A")?;
device.command("Perform task 1")?;
device.command("Pop setting A")?;
device.command("Perform task 2")?;
device.command("Push setting A")?;
device.command("Perform task 3")?;
device.command("Pop setting A")?; //<-- notice how setting A is load and unload multiple times
Run Code Online (Sandbox Code Playgroud)
我怎样才能像Python一样创建一个上下文管理器
some pseudo code of what I wanted
let setting_a : ContextManager = ContextManager::new(
enter : ||{
device.command("Push setting A");
},
exit: || {
device.command("Pop setting A");
}
)
with setting_a {
device.command("Perform task 1");
}
device.command("Perform task 2");
with setting_a {
device.command("Perform task 3");
}
with setting_a{
device.command("Perform task 4");
with setting_b{
device.command("Perform task 5");
}
device.command("Perform task 6");
}
Run Code Online (Sandbox Code Playgroud)
这是我的尝试,它有点适用于一些 rc/refcell 魔法,但它没有考虑到需要执行某些任务的上下文。
impl MyDevice{
/* ... */
pub fn with_param<F,O>(&mut self, param:Param,f:F)->O
where
F: Fn()->O
{
let last_param:Param = self.param.clone();
self.set_param(param);
let o = f();
self.set_param(last_param);
o
}
}
device.with_param(param, ||{/* ... */})
// v ----------- psuedo code ------------------------------ v
let setting_a : ContextManager = ContextManager::new(
enter : ||{
device.command("Push setting A");
device.command("Perform some task a-1");
},
exit: || {
device.command("Pop setting A");
device.command("Perform some task a-2");
}
)
// this doesn't not work on the with_param function,
// since it not only have to set param,
// but also need to perform some sub-task to enter and exit the context.
Run Code Online (Sandbox Code Playgroud)
1.组织此类代码的最明智的方式是什么?好吗Rc<refCell<_>>?macro?
2.我希望它是panic-proof的,我知道使用可能会引起恐慌,在这种情况下refCell我可以避免使用吗?refCell
3.如果我将来需要在线程之间传递这个怎么办?以及如何对其进行异步证明?
Cafce25 对高级函数的建议是一个不错的选择,并且在 Rust 的某些地方使用(例如作用域线程),但上下文管理器不存在且没有必要的原因主要是它们是RAII的次等兄弟Guards,这是 Rust 所青睐的,你可以在以下位置看到:
在这种情况下,您可以有一个settings方法(或每个设置的方法),该方法将在调用时推送设置,返回一个SettingsGuard在放置时弹出相同设置的方法。
为了使借用工作,并且为了安全和清晰起见,您可能希望SettingsGuard对设备有独占(/ unique / mut)引用,并允许通过自身执行命令(可能还有嵌套设置),产生类似以下内容:
dev.command("thing");
{
let mut g = dev.setting("g");
g.command("foo");
let mut stacked = g.setting("x");
stacked.command("bar");
drop(stacked);
g.command("x");
}
dev.command("bob");
Run Code Online (Sandbox Code Playgroud)
这样,只要推送一个设置,设备就会通过该设置的保护进行交互,这样命名(并证明命名的合理性)更容易,并且执行上下文更清晰(编译器将阻止通过父上下文发送命令,如只要守卫还活着)。
正如您在示例中所看到的,通过显式删除相应的保护(使用类似的东西drop(g);)或块的末尾(以这种方式使用匿名块是相对正常的),可以“弹出”设置。