50 个版本 (15 个稳定版)

2.0.3 2023 年 12 月 1 日
2.0.2 2023 年 6 月 30 日
1.1.0-pre.02023 年 5 月 24 日
1.0.9 2023 年 3 月 20 日
0.2.0 2021 年 6 月 24 日

#12 in Operating systems

Download history 876877/week @ 2024-04-20 798991/week @ 2024-04-27 808545/week @ 2024-05-04 848144/week @ 2024-05-11 831650/week @ 2024-05-18 824889/week @ 2024-05-25 855749/week @ 2024-06-01 805535/week @ 2024-06-08 823282/week @ 2024-06-15 829958/week @ 2024-06-22 782444/week @ 2024-06-29 815401/week @ 2024-07-06 841034/week @ 2024-07-13 883968/week @ 2024-07-20 849767/week @ 2024-07-27 822638/week @ 2024-08-03

3,539,447 个月下载量
3,733 个crate中使用 (26 个直接使用)

Apache-2.0…

63KB
805 行代码,不包括注释

io-lifetimes

一个低级 I/O 所有权和借用库

Github Actions CI Status crates.io page docs.rs docs

此库引入了 OwnedFdBorrowedFd 以及支持的类型和特质,以及针对 Windows 的相应功能,实现了安全的所有权和借用 I/O 生命周期模式。

这与 RFC 3128(I/O 安全性 RFC)相关,该 RFC 已合并。现在正在进行将此处开发的 OwnedFdBorrowedFd 类型以及 AsFd 特质移动到 std 的工作。

要快速了解,请查看代码示例

  • hello,这是此 API 的基本演示,手动进行低级 I/O,使用提供的示例 FFI 绑定
  • easy-conversions,展示了将 from_into 便利功能从 impl Into* 转换为 impl From* 的便利功能。
  • portable-views,展示了允许将文件描述符临时“查看”为任何拥有类型(如 File)的便利功能。
  • flexible-apis,展示了如何编写接受无类型 I/O 资源的库 API。
  • owning-wrapper,展示了如何实现一个包装 Owned* 类型的类型。

API 的核心非常简单,由两个主要类型和三个主要特质组成

pub struct BorrowedFd<'fd> { ... }
pub struct OwnedFd { ... }

pub trait AsFd { ... }
pub trait IntoFd { ... }
pub trait FromFd { ... }

impl AsRawFd for BorrowedFd<'_> { ... }
impl AsRawFd for OwnedFd { ... }
impl IntoRawFd for OwnedFd { ... }
impl FromRawFd for OwnedFd { ... }

impl Drop for OwnedFd { ... }

impl AsFd for BorrowedFd<'_> { ... }
impl AsFd for OwnedFd { ... }
impl IntoFd for OwnedFd { ... }
impl FromFd for OwnedFd { ... }

在Windows上,每个Fd对象都有对应的HandleSocket版本,还有一个特殊的HandleOrInvalid类型来处理Windows API中的不一致错误报告。

透明的魔法

这里是有趣的部份。BorrowedFdOwnedFdrepr(transparent)的,并持有RawFd值,而Option<BorrowedFd>Option<OwnedFd>在Rust >= 1.63中是FFI安全的,因此它们都可以在FFI直接使用

extern "C" {
    pub fn open(pathname: *const c_char, flags: c_int, ...) -> Option<OwnedFd>;
    pub fn read(fd: BorrowedFd<'_>, ptr: *mut c_void, size: size_t) -> ssize_t;
    pub fn write(fd: BorrowedFd<'_>, ptr: *const c_void, size: size_t) -> ssize_t;
    pub fn close(fd: OwnedFd) -> c_int;
}

有了这样的绑定,用户永远不需要触摸RawFd值。当然,不是所有的代码都会这样做,但这对能够这样做的代码来说是一个有趣的功能。这就是为什么要使用BorrowedFd而不是仅仅使用&OwnedFd的原因。

注意open函数使用Option<OwnedFd>作为返回值,表示它可能成功或失败。

Rust中的I/O安全性

I/O安全性特性在Rust 1.63中得到稳定。从这个版本开始或更高版本,io-lifetimes将使用并重新导出标准库中的类型和特性。在较旧版本中,io-lifetimes定义了自己的这些类型和特性的副本。

io-lifetimes还包括一些不在std中的特性,包括可移植特性AsFilelike/AsSocketlike等,From特性中的from_into_函数,以及视图

现有技术

有几个类似的crate:fdfiledescfiledescriptorowned-fdunsafe-io

其中一些提供了额外的功能,例如创建管道或套接字、获取和设置标志以及执行读写操作。io-lifetimes省略了这些功能,将它们作为单独的层提供。

大多数这些crate都提供了复制文件描述符的方法。io-lifetimes目前将其视为可以由上层提供的另一个功能,尽管如果存在这种操作是常见用例的情况,它也可以添加。

io-lifetimes的显著特点是它使用repr(transparent)来支持直接FFI使用,狭窄的优化使得Option也能支持直接FFI使用(在Rust >= 1.63中),生命周期感知的As/Into/From特性,它们利用Rust的生命周期系统,并允许安全的和经过检查的from_as_/into_函数,以及由其底层安全性提供的强大便捷功能。

io-lifetimes还完全支持Windows,以及Unix/Windows的可移植抽象,涵盖文件类和套接字类类型。

io-lifetimes 的 OwnedFd 类型与 fdFileDesc 类似。io-lifetimes 没有使用 close_on_drop 参数,而是使用 OwnedFdBorrowedFd 来分别表示释放和非释放句柄,这种方式在编译时而不是运行时进行检查。

io-lifetimes 的 OwnedFd 类型也与 filedescFileDesc 类似。io-lifetimes 的 OwnedFd 保留了值 -1,因此在它的 Drop 中不需要测试 -1,并且 Option<OwnedFd>(在 Rust >= 1.63)与 FileDesc 大小相同。

io-lifetimes 的 OwnedFd 类型也与 owned-fdOwnedFd 类似。io-lifetimes 没有实现 Clone,因为由于操作系统进程限制,复制文件描述符可能会失败,而 Clone 是一个不可失败接口。

io-lifetimes 的 BorrowedFdowned-fdFdRef 类似,但它使用生命周期参数和 PhantomData 而不是将原始文件描述符值转换为引用值。

io-lifetimes 的便利特性与 unsafe-io 类似,但 io-lifetimes 是基于它自己的 As*/Into*/From* 特性构建的,而不是通过 OwnsRaw 扩展 AsRaw*/IntoRaw*/FromRaw*,因此它们更简单、更安全。io-lifetimes 不包括 unsafe-io 的 *ReadWrite**HandleOrSocket* 抽象,并将这些作为由上层单独提供的特性。

支持的最小 Rust 版本 (MSRV)

此软件包目前适用于 [Debian 稳定版上的 Rust],目前是 Rust 1.63。此策略可能在未来的小版本发布中改变,因此使用固定版本 Rust 的用户应将此软件包的版本锁定为特定版本。

依赖项

~0–12MB
~128K SLoC