3 个不稳定版本
0.2.0 | 2023 年 7 月 9 日 |
---|---|
0.1.1 | 2021 年 10 月 6 日 |
0.1.0 | 2021 年 10 月 6 日 |
#2354 in Rust 模式
18KB
170 行
lifelink
使用泛型关联类型,从任何事物中删除协变生命周期参数。
[dependencies]
lifelink = { version = "0.1.1" }
类似于 cryo
,lifelink
允许你在生命周期不可预测的动态环境中使用结果类型,例如垃圾回收的脚本语言的运行时,或者需要 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