#生命周期 #引用 #所有权 #cryo #脚本语言

lifelink

从任何事物中删除协变生命周期参数,具有泛型关联类型

3 个不稳定版本

0.2.0 2023 年 7 月 9 日
0.1.1 2021 年 10 月 6 日
0.1.0 2021 年 10 月 6 日

#2354 in Rust 模式

MIT/Apache

18KB
170

lifelink

使用泛型关联类型,从任何事物中删除协变生命周期参数。

[dependencies]
lifelink = { version = "0.1.1" }

类似于 cryolifelink 允许你在生命周期不可预测的动态环境中使用结果类型,例如垃圾回收的脚本语言的运行时,或者需要 Any 的地方。与 cryo 不同,接口不仅限于原始引用:它可以通过 GATs 在具有协变生命周期参数的任何事物上工作。

generic_associated_types 功能已在 Rust 1.65 中稳定。如果需要稳定发布之前固定的夜间编译器,可以启用 nightly 功能,该功能会将适当的 feature 属性添加到包顶部。

示例

只有引用的简单情况

use std::thread::spawn;
use std::sync::atomic::{AtomicUsize, Ordering};
use lifelink::{lifelink, Lifelink, RefCtor};

let answer = AtomicUsize::new(0);

lifelink!(lifelink: RefCtor<AtomicUsize> = &answer);

{
    let guard = lifelink.get().unwrap();
    assert_eq!(0, guard.load(Ordering::Relaxed));
    guard.store(42, Ordering::Release);
}

assert_eq!(42, answer.load(Ordering::Acquire));

涉及多个生命周期参数、无关的类型参数和线程的更复杂示例

use std::thread::spawn;
use std::time::Duration;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::mpsc::channel;
use std::marker::PhantomData;
use lifelink::{lifelink, Lifelink, Ctor, Cov};

#[derive(Copy, Clone)]
struct Answers<'a, 'b, 'c, T> {
    first: &'a AtomicUsize,
    second: &'b AtomicUsize,
    third: &'c AtomicUsize,
    rest: T,
}

struct AnswersCtor<T> {
    _marker: PhantomData<T>,
}

impl<T: 'static> Ctor for AnswersCtor<T> {
    // The lifetimes can be unified here, due to covariance
    type Ty<'a> = Answers<'a, 'a, 'a, T>;
}

// An invocation of `lifelink::cov!` on the constructor type is required
// to prove covariance to the type system. This only compiles for types that
// can be proved by Rust to be covariant. See docs on the Cov trait for more
// details.
lifelink::cov!(<T: 'static> AnswersCtor<T>);

fn compute<'a, 'b, 'c>(answers: Answers<'a, 'b, 'c, ()>) {
    lifelink!(lifelink: AnswersCtor<()> = answers);
    let (send, recv) = channel();
    
    spawn(move || {
        let guard = lifelink.get().unwrap();
        guard.first.store(42, Ordering::Release);
        guard.second.store(42, Ordering::Release);
        guard.third.store(42, Ordering::Release);
        send.send(()).unwrap();
    });

    // Unlike `cryo`, `lifelink` does *not* attempt to wait until the `'static`
    // handle is dropped. As such, a way to wait for task completion external
    // to `lifelink` is required. See the Caveats section of README for more
    // details, and the rationale behind this decision.
    recv.recv_timeout(Duration::from_millis(20)).unwrap();

    assert_eq!(42, answers.first.load(Ordering::Acquire));
    assert_eq!(42, answers.second.load(Ordering::Acquire));
    assert_eq!(42, answers.third.load(Ordering::Acquire));
}

let first = AtomicUsize::new(0);
let second = AtomicUsize::new(0);
let third = AtomicUsize::new(0);

compute(Answers {
    first: &first,
    second: &second,
    third: &third,
    rest: (),
});

注意事项

Lifelink 只能始终提供共享/不可变引用。这是因为 Rust 默认允许移动,使得对具有生命周期参数的类型进行可变引用变得难以推理,除非降低到无用的程度,否则几乎无法正确使用。相反,用户必须以保持协变性的方式使用内部可变性(幸运的是,Rust 将在 Cov 实现中帮助证明)。

cryo 不同,lifelink 并不尝试等待 'static 处理程序被丢弃。如果没有作用域中的 Guard 在某个时刻,它非常乐意丢弃或展开一个 Deathtouch。这可能令人惊讶,但这是有意识的决策,以使 lifelink 能够与生命周期不可预测的环境协同工作,例如垃圾回收的脚本语言,在这种情况下,从行为不当的脚本中获得错误比死锁要好得多。因此,需要外部于 lifelink 等待任务完成的方法。

功能标志

  • nightly - 在包顶部添加 feature(generic_associated_types),允许在 1.65(功能稳定时)之前更早的夜间编译器上编译。

许可证

MIT 或 Apache-2.0

依赖项

~480–790KB
~13K SLoC