#drop #wrapper #into-inner

no-std with_drop

Nostd 包装器,用于将闭包用作自定义析构函数

3 个版本

0.0.3 2022年9月28日
0.0.2 2022年9月28日
0.0.1 2020年7月21日

#363 in 无标准库

Download history 385/week @ 2024-03-13 281/week @ 2024-03-20 250/week @ 2024-03-27 278/week @ 2024-04-03 312/week @ 2024-04-10 409/week @ 2024-04-17 734/week @ 2024-04-24 472/week @ 2024-05-01 299/week @ 2024-05-08 267/week @ 2024-05-15 288/week @ 2024-05-22 326/week @ 2024-05-29 337/week @ 2024-06-05 365/week @ 2024-06-12 338/week @ 2024-06-19 265/week @ 2024-06-26

1,358 每月下载量
用于 cproxy

BSD-3-Clause

10KB
58

with_drop

Nostd 包装器,可以用来执行自定义析构函数。

用法

Cargo.toml

[dependencies]
with_drop = "0.0.3"

代码中

use std::{cell::RefCell};
use with_drop::with_drop;

let drop_sum = RefCell::new(0);

{
  let mut v = with_drop(32, |x| { *drop_sum.borrow_mut() += x });

  // use value
  assert!(*v == 32);

  // Modify it
  *v = 42;
}

// Drop function should have been executed
assert!(*drop_sum.borrow() == 42);

动机

以下是一段代码示例

use std::{io::Result, process::Command, process::Stdio};

fn main() -> Result<()> {
  let child1 = Command::new("echo").arg("42").stdout(Stdio::piped()).spawn()?;
  let child2 = Command::new("echo").arg("23").stdout(Stdio::piped()).spawn()?;
  assert!(child1.wait_with_output()?.stdout == b"42\n");
  assert!(child2.wait_with_output()?.stdout == b"23\n");
  Ok(())
}

简单,对吧?我们启动两个子进程,收集它们的输出并与其比较一个值。但是,这个例子并不完全正确;它不是异常安全的。`std::process::Child` 文档指定,必须 `手动调用 wait()``(Child 不实现 Drop)才能正确清理进程(否则在 Linux 下将产生 `僵尸进程`)。

现在,如果你仔细查看上面的代码示例,可能会发现并不是每条代码路径都会调用 wait;如果一切按计划进行,wait 将会被调用,但如果由于失败的结果而提前退出,wait 将不会被 child1 或 child2 调用。

这种特性称为异常安全性(或结果安全性,因为 Rust 没有异常?);上面的代码示例不是异常安全的。我们可以使用 if 语句手动捕获所有这些情况,但这会使代码非常难以管理。最佳方案是 Child 类型实现 Drop 并自动等待进程。但它没有。在这种情况下,我们可以使用 `with_drop()` 创建一个包装器

use std::{io::Result, process::Command, process::Stdio};
use with_drop::with_drop;

fn main() -> Result<()> {
  let child1 = with_drop(Command::new("echo").arg("42").stdout(Stdio::piped()).spawn()?, |mut child| {
    // Explicitly ignoring errors; the command might not have been started or might
    // not have ended or might have yielded an error; in any case we don't mind because
    // we just care about cleaning up zombies.
    let _ = child.wait();
  });
  let child2 = with_drop(Command::new("echo").arg("23").stdout(Stdio::piped()).spawn()?, |mut child| {
    let _ = child.wait();
  });
  assert!(child1.into_inner().wait_with_output()?.stdout == b"42\n");
  assert!(child2.into_inner().wait_with_output()?.stdout == b"23\n");
  Ok(())
}

关于 `finally()` 怎么样?

能否用类似 finally() 的结构体代替?不可以!我们的 finally() 守护者必须存储 child 变量的可变引用,这将阻止我们调用 wait_with_output(借用检查器会报错)。

use std::{io::Result, process::Command, process::Stdio};
use with_drop::with_drop;

struct Finally<F: FnMut()> {
  f: F
}

impl<F: FnMut()> Drop for Finally<F> {
    fn drop(&mut self) {
      (self.f)();
    }
}

fn finally<F: FnMut()>(f: F) -> Finally<F> {
  Finally { f }
}

fn main() -> Result<()> {
  let mut child1 = Command::new("echo").arg("42").stdout(Stdio::piped()).spawn()?;
  let finally_guard1 = finally(|| {
    let _ = child1.wait();
  });
  let mut child2 = Command::new("echo").arg("42").stdout(Stdio::piped()).spawn()?;
  let finally_guard2 = finally(|| {
    let _ = child2.wait();
  });

  // error[E0505]: cannot move out of `child1` because it is borrowed

  //assert!(child1.wait_with_output()?.stdout == b"42\n");
  //assert!(child2.wait_with_output()?.stdout == b"23\n");

  Ok(())
}

测试

安装 clippy、rustfmt 和 nono

$ rustup component add rustfmt
$ rustup component add clippy
$ RUSTFLAGS="--cfg procmacro2_semver_exempt" cargo install cargo-nono

现在使用这些工具执行测试。

$ cargo build
$ cargo test
$ cargo clippy --all-targets --all-features -- -D warnings
$ cargo fmt -- --check
$ cargo nono check

许可协议

版权所有 © (C) 2020,Karolin Varner。保留所有权利。

在满足以下条件的情况下,允许重新分配和使用源代码和二进制代码,无论是否修改:

源代码的再分发必须保留上述版权声明、本许可清单以及以下免责声明。二进制形式的再分发必须在文档和/或随分发提供的其他材料中复制上述版权声明、本许可清单以及以下免责声明。未经具体事先书面许可,不得使用Karolin Varner的名字或其贡献者的名字来支持或推广由此软件派生出的产品。

本软件由版权所有者和贡献者“按原样”提供,并明确或暗示地放弃了任何明示或默示的保证,包括但不限于适销性和特定用途的适用性保证。在任何情况下,Softwear, BV不对任何直接、间接、偶然、特殊、示范性或后果性的损害(包括但不限于替代货物或服务的采购;使用、数据或利润的损失;或业务中断)承担责任,无论这些损害是由于何种原因引起的,无论基于何种责任理论(合同责任、严格责任或侵权责任,包括疏忽或其他),即使被告知了此类损害的可能性。

无运行时依赖