16个版本
0.2.4 | 2022年8月8日 |
---|---|
0.2.3 | 2022年8月5日 |
0.2.1 | 2022年7月17日 |
0.1.0 | 2022年5月30日 |
0.0.1-alpha | 2022年5月26日 |
#1264 在 过程宏
67,227 每月下载量
在 20 个crate中使用了(通过 nougat)
40KB
1K SLoC
::nougat
在稳定的Rust上使用(生命周期)GATs。
示例
#![forbid(unsafe_code)]
# use ::core::convert::TryInto;
#[macro_use]
extern crate nougat;
#[gat]
trait LendingIterator {
type Item<'next>
where
Self : 'next,
;
fn next(&mut self)
-> Option<Self::Item<'_>>
;
}
struct WindowsMut<Slice, const SIZE: usize> {
slice: Slice,
start: usize,
}
#[gat]
impl<'iter, Item, const SIZE: usize>
LendingIterator
for
WindowsMut<&'iter mut [Item], SIZE>
{
type Item<'next>
where
Self : 'next,
=
&'next mut [Item; SIZE]
;
/// For reference, the signature of `.array_chunks_mut::<SIZE>()`'s
/// implementation of `Iterator::next()` would be:
/** ```rust ,ignore
fn next<'next> (
self: &'next mut AChunksMut<&'iter mut [Item], SIZE>,
) -> Option<&'iter mut [Item; SIZE]> // <- no `'next` nor "lending-ness"! ``` */
fn next<'next> (
self: &'next mut WindowsMut<&'iter mut [Item], SIZE>,
) -> Option<&'next mut [Item; SIZE]> // <- `'next` instead of `'iter`: lending!
{
let to_yield =
self.slice
.get_mut(self.start ..)?
.get_mut(.. SIZE)?
.try_into() // `&mut [Item]` -> `&mut [Item; SIZE]`
.expect("slice has the right SIZE")
;
self.start += 1;
Some(to_yield)
}
}
fn main() {
let mut array = [0, 1, 2, 3, 4];
let slice = &mut array[..];
// Cumulative sums pattern:
let mut windows_iter = WindowsMut::<_, 2> { slice, start: 0 };
while let Some(item) = windows_iter.next() {
let [fst, ref mut snd] = *item;
*snd += fst;
}
assert_eq!(
array,
[0, 1, 3, 6, 10],
);
}
调试/跟踪宏展开
你可以让宏通过中间生成的文件来执行,以便获得良好的错误信息和可以打开并检查的文件,同时剩余的宏未展开以便于阅读,方法是通过
-
启用此依赖项的
debug-macros
Cargo功能[dependencies] ## … nougat.version = "…" nougat.features = ["debug-macros"] # <- ADD THIS
-
设置环境变量
DEBUG_MACROS_LOCATION
为某个绝对路径,宏将在此路径下写入生成的文件。
演示
宏是如何工作的?
点击此处查看实现说明
一些历史背景
-
2021/02/24: 使用
for<'lt> Trait<'lt>
作为超特例来模拟GATs的实验- (我怀疑甚至可能还有在URLO之前的实验和使用,但我现在找不到它们)
这已经让GATs几乎完成,但有两个问题,我当时对此表示了不满 😅
-
Trait<'lt>
嵌入 所有 关联项,包括方法,而不仅仅是关联的“泛型”类型。这可能会带来问题,如果这些其他项依赖于关联类型是完全泛型的,正如我在2021年3月6日的这里所观察到的。
-
我无法表达
where Self : 'next
GAT界限。
-
2022/03/08:我在这篇官方文章中正式提到了通过类型如
&'lt T
的隐式界限来解决"late/for
-量化where T : 'lt
"子句的替代方法。
点击查看更多上下文
-
这个想法不是我自己想出来的;它有点模糊,但我记得URLO用户
steffahn
在这方面做了很多工作(例如,这个问题),而且我清楚地记得Kestrer
在社区Discord中指出隐式界限黑客攻击。- 对于那些感兴趣的人来说,我后来使用了这项技术,在一篇非常详细的URLO帖子中,我敢肯定你会对此感兴趣,来解决在更高阶闭包上下文中“过于严格的寿命界限”问题。
所以,在那时,所有这些成为了URLO的一些常客(如
steffahn
和quinedot
)之间的“高级知识”,但从未真正付诸实践:这个想法是等待“合适的解决方案”,即GATs。 -
尽管如此,我开始考虑这个非常规的crate的想法,当时被称为
autogatic
-
遗憾的是,这个提案遭到了冷遇:GATs即将稳定,因此一个自动化替代方案的工具被认为是没有用的。
所以我一直在等待。最后,稳定性问题被打开,然后...某种程度上的“关闭”(更准确地说,推迟到一系列方面可以整理出来,更多信息请参阅该问题)。说实话,我认为现在不稳定的论点相当合理和有根据,即使我仍然希望这个问题在中期内得到稳定。
这一切都证明了我的
autogatic
想法是合理的,因此我致力于实现我心中的原型想法:nougat
诞生了。
-
这时,用户
Jannis Harder
提出了一个关于GATs填充的另一个实现/替代方案。-
使用“标准GAT替代方法”来定义HKT特质
trait WithLifetime<'lt> { type T; } trait HKT : for<'any> WithLifetime<'any> {} impl<T : ?Sized + for<'any> WithLifetime<'any>> HKT for T {}
-
然后,用
type Assoc : ?Sized + HKT;
- 并使用
<Self::Assoc as WithLifetime<'lt>>::T
来代替Self::Assoc<'lt>
,在解决具有具体生命周期的类型时。
- 并使用
-
因此,在实现者方面可以使用
impl LendingIterator for Thing { // type Item // <'next> // = &'next str // ; type Item = dyn for<'next> WithLifetime<'next, T = &'next str >; // formatted: type Item = dyn for<'next> WithLifetime<'next, T = &'next str>; }
- (或者使用
for<…> fn…
指针,但实际上它们并不像dyn for<…> Trait
那样工作得很好)
- (或者使用
这种方法有一些缺点(隐式界限更难(但不是不可能!)挤进去),并且当
Assoc<'lt>
有自己的界限时,似乎需要一种专门的HKT
trait,它具有对T
的这种界限。话虽如此,这种基于
HKT
的方法有一个优点,即它是唯一一个能够在一定程度上与dyn
友好的(-ish),而“经典解决方案”方法则不是这样。请参阅下面的 Sabrina Jewson 的博客文章,以了解这两种方法的更深入比较。
-
实际解释
当我准备花几个小时详细说明这些技巧时 😅,幸运的是,我得知有人已经做了所有这些工作,而且文笔比我好得多:Sabrina Jewson
🙏。她写了一篇非常完整和详尽的博客文章,关于 GAT、它们的稳定补丁,以及它们是如何相互比较的(有趣的是,GAT 当前 比 它们的补丁更差,因为由于编译器错误,每次向 GAT 添加一个 trait 约束时,相关的 GAT 都不得不成为 : 'static
,没有任何实际原因,只是编译器在这件事上出了问题)。。
以下是该博客文章的链接,直接指向该 crate 正在使用的工作区,但您也可以删除锚点并阅读全文,它绝对值得一看
📕 Lifetime GAT 的更好替代方案 – 由 Sabrina Jewson 编写 📕
限制
lib.rs
:
该 crate 不打算直接使用。请使用 https://docs.rs/nougat。
依赖项
~1.5MB
~37K SLoC