Rust: const 泛型的跟踪问题 (RFC 2000)

创建于 2017-09-15  ·  202评论  ·  资料来源: rust-lang/rust

rust-lang/rfcs#2000 的跟踪问题

更新:

  • 2019 年 5 月 2 日: https :
  • 2019 年 10 月 19 日: https :
  • 2020 年 1 月 2 日: https :
  • 2020 年 7 月 22 日: https :

如果你想帮帮忙,看看在开放常量仿制药的问题,并在入门随意平@varkor,@eddyb,@yodaldevoid,@ OLI-OBK或@lcnr求助!


阻塞稳定:

  • [ ] 设计:

    • [ ] 使用默认参数解析 const 和类型参数的顺序

    • [ ] 决定什么是统一抽象 const 表达式的最佳 UX / 实现成本平衡。

    • [ ] 我们如何确定 const 表达式的格式良好。

  • [x] 实施
  • [ ] 文档

    • [] rustc 指南


其余实施问题:

  • [ ] 解决各种FIXME(const_generics)注释。
  • [ ] 解决规范化/惰性规范化的问题。
  • [ ] 研究对模式中 const 参数的处理。
  • [ ] 添加更多测试。
  • [ ] 实现 const 参数的默认值 ( FIXME(const_generics:defaults) )。
  • [] 修复其他A-const-generics 问题
  • [ ] 审计has_infer_types
  • [x] 禁止包含参数的 const 参数的复杂表达式(目前),例如{X * 2}
  • [ ] 审计诊断(例如 https://github.com/rust-lang/rust/pull/76401#discussion_r484819320)。
A-const-fn A-const-generics A-typesystem B-RFC-approved C-tracking-issue F-const_generics T-compiler T-lang requires-nightly

最有用的评论

这是迄今为止在 const 泛型方面的进展摘要。


在 const 泛型的工作可以正确开始之前,需要进行一些重构。 @jplatte通过https://github.com/rust-lang/rust/pull/45930承担了这项任务@jplatte然后开始致力于const 泛型的主要实现,但发现他们没有足够的时间继续。

@yodaldevoid和我从@jplatte停止的地方开始,但很快发现一般处理泛型参数的方式阻碍了取得进展。 因此开始了一系列更改以改进整个代码库中处理泛型的方式: https : //github.com/rust-lang/rust/pull / 48452https://github.com/rust-lang/rust/pull/48523https://github.com/rust-lang/rust/pull/51880。

完成后,const 泛型的实现就可以认真开始了。 从那以后, @yodaldevoid和我一直在缓慢但肯定地逐渐增加对 const 泛型的支持: https : //github.com/rust-lang/rust /pull/58503 , https://github.com/rust-lang/rust/pull/58581 , https://github.com/rust-lang/rust/pull/58583 , https://github.com/rust -lang/rust/pull/59170https: //github.com/rust-lang/rust/pull/59355、https : //github.com/rust-lang/rust/pull/59415、https ://github .com/rust-lang/rust/pull/60058https://github.com/rust-lang/rust/pull/60280https://github.com/rust-lang/rust/pull/60284和大多数最近https://github.com/rust-lang/rust/pull/59008。 (这些主要是从主要的 const 泛型拉取请求中分离出来


现在是什么状态? 一些const 泛型测试现在可以工作了 🎉然而,仍然有一些不工作,并且在整个代码中还有许多FIXME s 我们仍然需要解决。 不过,我们现在已经接近尾声了,如果您想提供帮助,我们正处于一些唾手可得的果实的阶段。

  • 首先,我们需要更多的测试。 到目前为止,只有少数const 泛型测试,但我们想要更多。 目前,因为我们知道一些问题,所以我们不需要错误报告,但我们确实需要通过测试。 如果您想出一个有效的测试用例(并且看起来与现有测试不太相似),请随时打开拉取请求以添加它。
  • 其次,如前所述,有许多FIXME(const_generics)散布在整个代码中。 我们正计划通过我们的方式解决它们,但是@yodaldevoid和我只有这么多时间,所以如果您认为您可以解决一个问题,请继续(尽管您可能想发表评论,所以我们不会” t 重复努力)。

在 const 泛型准备好进行适当的测试之前,我已经概述了一些剩余的实现问题,在顶帖中,粗略地了解剩下的工作。

这需要时间,但我们一直在稳步取得进展,并希望跟上步伐。 终于看到事情开始到位是令人鼓舞的!

所有202条评论

44275 添加了一个ConstEvaluatable谓词, WF([T; expr])现在需要ConstEvaluatable(expr) ,因为expr是惰性求值的(即使它只是一个整数文字)。 实现这个谓词需要表达式成功求值,而规范化忽略错误并简单地保留它找到的Unevaluated表达式,不受影响,这或多或少是关联类型投影发生的情况。 我希望相同的系统可以扩展到常量泛型。

@EpicatSupercell已表示有兴趣从事此工作,我将在初始实施过程中指导他们。 但是,由于#44275 中描述的限制,我们不能走得太远。

也就是说,我们需要@nikomatsakis的惰性归一化,以允许嵌入在类型中的常量表达式观察范围内的边界(来自它们所在的函数/类型定义/impl/等项目),而不会在一半时间产生循环依赖。

实现路径点(为了更直接的指导,在 Gitter 上寻找@eddyb或在 IRC 上寻找eddyb ):

请注意,所有这些都应该允许impl<T, const N: usize> Trait for [T; N] {...} ,但实际上不会将常量表达式传递给类型/函数,例如ArrayVec<T, 3>

我想尝试一下:)

@jplatte @eddyb 有这方面的消息吗?

@samsartor在主要实现工作之前必须进行重构。 现在差不多完成了(我正在等待反馈)。 我实际上不知道在那之后还有多少工作。 解析 const params 是我在重构之前最初开始的。 它还没有完成,但我应该能够相对较快地完成它。

@jplatte如果您可以链接拉取请求,那就太好了。 我无法找到它。

目前还没有公关。 我所有的作品都可以在这里找到。

到目前为止做得很好

现在有一个基础工作的 PR( Generics重构):#45930

我认为这已经包括在内,但最好处理 C++ 样式折叠以处理具有不同长度的线性代数样式 n 维数组,即 4x4 [[f64;4];4] 或 4x3x5x6 维数组 [[[[] f64;6];5];3];4] 并且能够正确包装和生成专门的方法,以及正确尺寸向量标量的正确特征实现等。请参阅https://gist.github.com/huhlig /8b21850b54a75254be4b093551f8c2cb一个基本的例子。

我不记得之前有人为 Rust 提出过折叠表达式,更不用说作为这个 RFC 的一部分了。 但既然这是 Rust,有什么理由不能将折叠表达式实现为普通宏,而不是新的专用语法?

#45930 合并后下一步是什么? @jplatte

@kjaleshire下一步是实现对 const 泛型的解析(请参阅@eddyb的进一步评论)。 在很明显重构中的重构是必要的之前,我已经开始了这方面的工作。 我现有的工作可以在这里找到; 自从重构 PR 被合并后,它还没有被重新定位。

@jplatte我相信你是想提到 @kjetilkjeka。

感谢更新! 我敢肯定,我绝不是唯一一个期待此功能的人。 保持良好的工作!

@jplatte不想不耐烦,但是有没有这方面的工作? 你需要帮助吗? 有人应该帮忙吗?

@est31对不起。 有一段时间没有时间做这个了,我不完全确定我什么时候有时间。 也许最好让其他人从我离开的地方继续。 我认为解析部分主要完成了。 代码中有两个地方我不确定在通用参数是 const 参数的情况下该怎么做:

也没有任何解析 const 泛型的测试(以及我同时更新的漂亮打印代码)。 不确定我可以提供哪些其他信息需要/有助于其他人继续处理此问题,但如果我的代码有任何不清楚的地方,请随时联系我。

@jplatte ~ None第一个,第二个什么都没有~~ (cc @pnkfelix @nikomatsakis)

编辑:在任何一种情况下都无事可做。

@eddyb对于第一个链接片段,您确定应该返回None吗? 我可能不明白这里发生了什么,但在我看来,什么都不应该做。 如果None被返回,其他可能不安全的参数将被跳过。

@yodaldevoid对不起,你说得对,我没有意识到这是在Generics

我想从@jplatte停止的地方开始。 我已经为此工作了几天,通过@eddyb的指导说明,因为我有时间看看我是否可以到达任何地方。 在这一点上,我已经进入了“使用/语义”部分。 我可能很快就会通过其中一个聊天来提问。

@yodaldevoid

@yodaldevoid :你愿意合作吗? 我也一直在对这个问题做一些调查(😅)——也许我们可以填补彼此工作中的空白。 (你可以在这里看到我

@varkor看起来你比我领先。 我当然愿意合作。 我会在某个时候尝试在 IRC 上抓住你,同时看看我是否已经完成了你还没有完成的任何事情。

这方面有什么进展吗?
我正在为嵌入式编写代码,我真的需要 const 泛型。

@qwerty19106
这方面正在取得进展,尽管进展缓慢。 @varkor和我一直在做这件事,因为我们有时间。 本周我取得了一些进展,我们在隧道尽头看到了基本使用的曙光。

除了实现 const 泛型之外,我们 (varkor) 还做了一些清理工作以使这一切成为可能(参见 #48149 和 #48523)。 我相信当前的计划是在引入 const 泛型之前等待这两个拉取请求通过,但 varkor 可以对此进行更多说明。

我真的理解您对用于嵌入式工作的 const 泛型的需求。 我开始这样做是因为我也非常想要 const 泛型,这样我就可以清理大量的嵌入式代码并使类型安全。

TL;DR:正在取得进展,但这很复杂。 我觉得你在嵌入式前端。

对于“文档”复选框,最好在rustc 指南中获得一些

感谢@yodaldevoid的回复。 我会期待你的工作结束。

为那些好奇的人更新(因为我也很好奇)。 回复:上面提到的两个 PR:#48523 已经合并,#48149 正在稳步推进。

@mark-im 好东西! @varkor 的出色工作。 ETA大概是什么时候,你知道吗? :-)

干杯,@flip111。

看起来第二个大重构 PR #48149 合并了 :)

/抄送我

下一个@varkor PR #51880

只是想快速更新一下,因为我知道有些人一直在询问 const 泛型的进展情况。

为了提供一些背景信息,在 3 月份@yodaldevoid和我有一个几乎可以正常工作的初始实现(大部分实现似乎已经完成,我们只是清理了一些剩余的崩溃)。 但是,我们正在处理的分支是 pre-miri。 当 miri 被合并(以及在随后的一些 PR 中)时,处理常量评估的代码发生了重大变化,这意味着我们所做的很多事情都已经过时了。

最重要的是,决定在添加 const 泛型之前,一般需要清理泛型参数代码,这既是为了提高可读性,也是为了在某些情况下我们忘记处理 const 泛型的错误更难处理制作。 这是在做https://github.com/rust-lang/rust/pull/48523https://github.com/rust-lang/rust/pull/48149并将于完成的https:// github上。 com/rust-lang/rust/pull/51880。 这些比我最初预期的要多一些,而且他们花费的时间比估计的要长一些。

与此同时, @yodaldevoid和我一直致力于使我们的原始实现与 rustc 中的所有后续更改兼容。 这花了一段时间,但我们已经到达那里(尽管有一个长期存在的问题,即从来没有像您预期的那样多的时间)。 我希望我们很快就会在这方面得到一些好消息。 (同时, https://github.com/rust-lang-nursery/chalk取得了良好的进展,应该可以解决@eddyb最初描述的一些困难。)


有人问他们如何提供帮助:我认为在这个阶段,我们会更容易尝试完成初始实施,然后看看哪些部分需要注意。 一旦准备就绪,我们将需要进行大量测试,在不同的地方使用 const 泛型(包括无效的,用于错误消息),所以这绝对是我们可以在很多帮助下做的地方! 我们会在发生这种情况时通知您!

对不起,如果这不是合适的地方,但我有一个关于抽象 const 表达式相等的建议。 一般来说,它直接减少到完全依赖的类型和不可判定的领域。 然而,在 rust 中,一切最终都会用具体的值/类型实例化,因此我们可以断言一些相等性并将它们的检查推迟到单态实例。

我的意思是,检查抽象 const 表达式是否相等的一种相对简单的方法如下:

  • 自动处理句法相等(即N+1 == N+1应该开箱即用)
  • 允许用户在定义时添加等式,例如N+M == M+N (可能在where子句中?)。 这些方程可以用于等式检查(使用某种形式的同余闭包)。 不使用这些提供的方程进行类型检查的定义被拒绝。
  • 在一个单态展开点,所有的方程都可以通过实际计算const expr来检验,const expr不再是抽象的。 这里有一个设计选择:如果方程简化为false ,则可能存在编译错误(“方程是公理”)或特征无法实例化(“方程是约束”)
  • 在参数化扩展点,这些方程被继承:如果你有一个由N参数化的函数f N其中N+0==N ,这个方程不能在调用者g丢失g每个单态位置进行检查。

这种方法的优点是 rustc 本身不需要定理证明器、SMT 求解器或算术重写器。 “仅”对用户提供的类型、模别名和模一组方程的句法相等性检查。
缺点是它对用户来说更加手动,因为必须明确添加诸如M+N=N+M类的看似明显的等式。

例子:

/// this works directly because of syntactic checks
fn add_end<T:Copy, const N: usize>(a: &[T;N], b: T) -> [T;N+1] {
  let x : [T;N+1] = [b;N+1];
  x[0..N] = a;
  x
}

/// this should work already
fn append<T:Copy, const M:usize, const N:usize>(a: &[T;M], b: &[T;N])
  -> [T;{M+N}]
{ … }

/// Here the equation M+M==N must be carried over or checked whenever this function is used
fn sum_pairwise_append<const M: usize, const N: usize>(a: &[i32, M],b: &[i32;N])-> [T;N]
  where M+M == N {
  let mut res : [i32; N] = append(a,a);
  for i in 0 .. N { res[i] += b[i] };
  res
} 


fn main() {
  let a: [i32; 2] = [1;2];
  let b: [i32; 4] = [2;4];
  let _ = sum_pairwise_append(a, b);  // need to check 2+2=4 
}

作为公理的方程

这意味着e1 == e2应该始终为真,因此它不是真正的where约束,而更像是类型检查器可以使用的assert_eq!(e1,e2) 。 在这里,实现者承诺这总是正确的,并且如果他们提供反驳等式的参数,则将她的用户暴露在编译错误中。

作为约束的方程

这里的子句where e1 == e2是成功使用这个参数化特征/函数必须满足的约束。 这意味着e1 == e2不必总是成立,这对于仅在域上为真的方程可能很有趣,例如(x*y) / z == (y*x) / z将无法实例化z==0

@c-cube 我们已经有一个用于“隐式where子句”的系统,至少对于从“ WF (格式良好)规则”派生的函数,即如果您定义了fn foo<'a, 'b>(x: &'a &'b i32) {}然后它要求调用者满足'b: 'a (由于需要WF(&'a &'b i32) -> &'b i32: 'a -> 'b: 'a )。

我们可以重用该系统(实际上这已经实现了)将签名(形式为“此常量表达式计算成功”)where子句。

您描述的大多数其他内容似乎都与计划中的内容很接近(包括一种“句法相等”的形式),但我们不想要“单态化时间”检查,我们可以采用其他方式(隐式或通过where子句)来“将约束传播到实例化器”,然后我们会在约束“仍然过于通用”的情况下产生错误(因此不知道它们是否成立),或者如果我们可以评估它们并且它们碰巧不成立。

您所描述的其他大部分内容似乎都与计划中的很接近

@eddyb你知道任何讨论这些计划的参考帖子吗?

@flip111可能是一些 RFC。 也许@withoutboats知道得更好。

# 51880 完成:多田::多田:

@withoutboats @ miri获得某种基本形式的符号求值时阻止N + 1 (例如[T; N + 1] )等表达式的正确统一?

我认为我们已经有了一种对其进行编码的机制,当@varkor添加ConstValue::{Infer,Param} ,我们可以使用它来跟踪“(浅)有效但未知”(符号)值,然后还包括值(主要是整数)操作和调用。

任何非assert控制流决策仍然需要已知值,但assert本身也许可以具体化为AssertThen(condition, assert message, success value)
conditionsuccess value都可以是象征性的!)

能够将符号值导出为ty::Const意味着我们可以在 miri 之外实现一些统一,将(大多数?)操作视为不透明。

我们必须小心不要假设任何推理变量,但是对于例如N + 1我认为我们可以支持将两个Add(Param(N), Bits(1))统一在一起。

@varkor我认为应该只使用惰性规范化而不是统一的测试用例:

/*const*/ fn append<const N: usize, T>(xs: [T; N - 1], x: T) -> [T; N] {
    let new = MaybeUninit::<[T; N]>::uninitialized();
    unsafe {
        let p = new.as_mut_ptr() as *mut T;
        (p as *mut _).write(xs);
        p.add(N - 1).write(x);
    }
    new.into_inner()
}

那只会起作用,因为:

  • N - 1在类型级别只出现一次

    • 一切都可以指代那个表达,不透明

    • 潜在的解决方法: type ArrayWithoutLast<T, const N: usize> = [T; N - 1];

  • 它位于签名中的参数位置

    • (不记得返回位置是否也有效。@nikomatsakis?)

    • 调用者可以通过提供N具体值来证明它是 WF

    • “冒泡”WF要求需要统一/ ArrayWithoutLast

    • 其他用途需要“嵌入where子句中”,例如[T; N - 1]: Sized



      • 甚至可以滥用where [T; N - 1]: (今天会被忽略吗?哎哟!)


      • 再次通过where ArrayWithoutLast<T, N>: ...绕过统一



所以总的来说,我们可能可以依靠 WF 规则来将 const 表达式的“验证”强制给用户,但我们仍然希望对其他事情进行统一(包括不需要像ArrayWithoutLast这样的黑客)。

53645

源自对度量单位的讨论的小问题。 如果类型泛型而不是常量不直接依赖它,我们该怎么办? 例如:

struct Foo<T, const N: usize> {
    a: T,
    // Will such array be "the way" to handle this problem?
    // Or do we want some kind of `std:marker::PhantomConstData`? (which can be a simple
    // type alias to the same array)
    // Or maybe make `PhantomData` to accept constants?
    _phantom: [(), N],
}

_phantom: [(), N],

我假设您的意思是[(); N] ,但这仅适用于usize

我认为有一个特殊的marker::PhantomConst允许任何const类型是有意义的。

嗯……重新阅读 RFC 并记住最新的评论,我想知道:这样的事情会被允许吗?

struct Foo<T, const V: T> {
    _phantom: PhantomConst<T, V>,
}

我看不到它在任何地方被禁止,但我认为它至少值得一个例子。

@Ekleog :据我所知,const 参数类型最初可能不依赖于类型参数(尽管它对于扩展肯定有意义)。 (如果我们允许,实现会更棘手,所以从最简单的版本开始是有意义的。)

这方面的进展如何? 我们知道这应该在每晚发生的大概时间吗?

@Zauberklavier一旦完成此拉取请求。 引用它:

有很长的路要走

@newpavlov我假设常量参数将被允许而不用在字段中。
必须使用类型参数的原因是差异,但常量没有这个问题。

@eddyb这也是我从 RFC 讨论中想起的。

@varkor所以这意味着@cuviper提出的PhantomConst在这个 RFC 的当前状态中不存在......对吗? 尽管最新的评论似乎指出无论如何都不需要PhantomConst

const 参数会影响它们涉及的类型参数的方差吗?

目前我相信 const 泛型不能在它们的类型中包含类型参数。

我不清楚它的第一个工作版本将能够做什么。

例如,此注释中的代码是否会编译:
https://github.com/rust-lang/rfcs/pull/2581#discussion_r230043717

如果该代码被编译,它将是一种在获得语法之前对常量强制约束的方法。

@rodrimati1992您可能只需执行(): IsTrue<{N < 128}>而不使用单独的类型。
我猜你想重用约束并让它真正起作用? (因为最初复制N < 128表达式不会使它起作用)
你可以使用trait Lt128<const N: usize> = IsTrue<{N < 128}>; ,我猜。
我认为也可以选择只写where [(); 128 - N], (但我不确定我们保证会恐慌)。

@rodrimati1992您可能只需执行(): IsTrue<{N < 128}>而不使用单独的类型。
我猜你想重用约束并让它真正起作用? (因为最初复制N < 128表达式不会使它起作用)
你可以使用trait Lt128<const N: usize> = IsTrue<{N < 128}>; ,我猜。
我认为也可以选择只写where [(); 128 - N], (但我不确定我们保证会恐慌)。

那么,使用 trait 别名我可以重写的是这个吗?:

trait AssertLessThan128<const N:usize>=
    Assert<{N<=128}, (
        Str<"uint cannot be constructed with a size larger than 128,the passed size is:",
        Usize<N>>
    ) >;

与 Assert 的想法trait 使用类型错误来打印嵌入在类型中的错误消息,在 AssertLessThan128 的情况下是:

(Str<"uint cannot be constructed with a size larger than 128,the passed size is:",Usize<N>>)

我希望它比where [(); 128 - N],更有帮助,因为它会在错误消息中告诉您为什么会发生编译时错误。

你也可以在常量中做if !(N < 128) { panic!("...") } ,我想?

我认为 std::fmt 架构在const trait约束(或类似的东西)到来之前不会存在?

有了这个东西,你可以有包含多个值的错误消息(嵌入在类型中)。

也许最好等待 const 泛型来讨论这个,因为它会更容易显示可执行示例。

@rodrimati1992https://github.com/rust-lang/rfcs/blob/master/text/2345-const-panic.md ,在它之前将有一个特殊情况来获取 const panic!()可以通过其他功能实现(猜测它最初可能不允许自定义值的格式)。

@rodrimati1992啊,我明白了,这确实是一个聪明的把戏!

这是什么状态?

这是对的。 为了给那些不遵循 PR #53645 的人提供快速更新,const 泛型至少在一个简单的用例中编译和工作。 剩下的就是完成其他用例的代码生成,包括 arrys 中的 const 泛型,以及一些错误输出清理。 之后,PR 应该准备好合并,人们可以开始玩它了。

可能是题外话,但这是否允许Chunks和相关方法的变体或分支使Item成为编译时固定大小的数组而不是切片? 这将允许for循环解构/绑定到一个无可辩驳的模式,该模式将块中的元素映射到变量,例如当前失败的for (first, second) in arr.chunks(2) ,我猜(没有任何理由诚然)这将允许在某些用例中进行更多优化。

@jeffvandyke您可能会专门考虑ChunksExact 。 这样的更改会破坏 API,因此无法完成。 我们可以在未来添加提供数组引用的新 API,但我们不能破坏现有的 API。

@jeffvandyke您可能会专门考虑ChunksExact 。 这样的更改会破坏 API,因此无法完成。 我们可以在未来添加提供数组引用的新 API,但我们不能破坏现有的 API。

我只是在等待这个实现和稳定,然后再提出一个 PR,除了ChunksExact和它的变体之外,还添加了这样的 API :)

运行时和编译时变体都有其用例,您并不总是提前知道您的块大小。 优化方面,如果您将ChunksExact与常量一起使用,根据我的测试,它应该或多或少相同。 编译器可以优化掉所有的边界检查。

有待实施和稳定

我建议不要等待稳定,因为这样的 API 将是一个更好的用途,可以帮助锻炼此功能以实现稳定。

我猜这对impl块还不起作用? 我试过

#![feature(const_generics)]

struct The<const Val: u64>();

impl<const Val: u64> The<Val> {
    fn the() {
        println!("{}", Val);
    }
}

但是,正如确实警告过的那样,编译器崩溃了

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
   0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
   1: std::sys_common::backtrace::_print
   2: std::panicking::default_hook::{{closure}}
   3: std::panicking::default_hook
   4: rustc::util::common::panic_hook
   5: std::panicking::rust_panic_with_hook
   6: std::panicking::continue_panic_fmt
   7: rust_begin_unwind
   8: core::panicking::panic_fmt
   9: core::slice::slice_index_order_fail
  10: rustc_resolve::Resolver::resolve_ident_in_lexical_scope
  11: rustc_resolve::Resolver::resolve_path
  12: rustc_resolve::Resolver::resolve_path_without_parent_scope
  13: rustc_resolve::Resolver::smart_resolve_path_fragment
  14: rustc_resolve::Resolver::smart_resolve_path
  15: <rustc_resolve::Resolver<'a> as syntax::visit::Visitor<'tcx>>::visit_ty
  16: syntax::visit::walk_generic_args
  17: syntax::visit::walk_ty
  18: rustc_resolve::Resolver::with_generic_param_rib
  19: rustc_resolve::Resolver::resolve_item
  20: rustc_resolve::Resolver::resolve_crate
  21: rustc::util::common::time
  22: rustc_interface::passes::configure_and_expand_inner
  23: rustc_interface::passes::configure_and_expand::{{closure}}
  24: <rustc_data_structures::box_region::PinnedGenerator<I, A, R>>::new
  25: rustc_interface::passes::configure_and_expand
  26: <rustc_interface::queries::Query<T>>::compute
  27: <rustc_interface::queries::Query<T>>::compute
  28: <rustc_interface::queries::Query<T>>::compute
  29: rustc_interface::queries::<impl rustc_interface::interface::Compiler>::prepare_outputs
  30: rustc_interface::interface::run_compiler_in_existing_thread_pool
  31: <std::thread::local::LocalKey<T>>::with
  32: <scoped_tls::ScopedKey<T>>::set
  33: syntax::with_globals

该错误与const中的impl<const Val: u64>因为删除该部分会导致其他错误但不会崩溃。

但是支持 Rust 甚至考虑这个功能。 我不知道它是否可行,但语法似乎很自然,我选择了它,而且 rustc 说它存在:)

我对它不起作用并不感到惊讶,因为在我们说话时,这个备受期待的功能仍在通过编译器进行探测(参见例如 #59008 和 #58581 以及早期关于 #53645 的工作,由于 PR 太大而被放弃,但仍作为跟踪器保持打开状态以宣布进度)。

但是,我不确定当前的实现存根是否应该期望越界切片访问。 @varkor @yodaldevoid ,你能看看吗?

是的,警告是正确的:const 泛型还不能以任何形式运行。 在准备开始使用之前,还有一些拉取请求。

抱歉,如果这不是提问的正确地方,但我找不到更好的地方。 只有2个问题:

  1. 单个函数可以有条件地const吗? 例如,一个函数可以有 2 个签名,例如:

    const fn foo<A: const T>(x: T)  // `A` const implements `T`
    fn foo<A: T>(x: A)              // `A` implements `T` normally
    

    在这种情况下, foo是 const iff A: const T ; 如果A没有 const 实现T ,它仍然满足界限,但foo不再是 const 。 对于更复杂的示例(例如where A::Output : Bar ),作者能够指定任何泛型边界也很重要。 一个很好的例子就是简单的算术:

    // This only accepts types that const implement `T`
    const fn square_const<T: const Mul>(x: T) -> T {
      x*x
    }
    
    // This accepts types that implement `T` any way, but it is not const
    // This function behaves identically to `square_const`
    // But has a different signature and needs a different name
    fn square<T: Mul>(x: T) -> T {
      square_const(x)
    }
    
    let a: u8 = 5;
    let b: FooNumber = FooNumber::new();
    square_const(a); // `u8` const implements Mul
    square(b); // `FooNumber` implements `Mul` normally, so we need a separate function?
    

    我强烈认为肯定应该有办法做到这一点,我很惊讶 RFC 中没有提到它(除非我错过了?)。

  2. _[不太重要]:_ 有没有办法在 const 函数体中检测我们是在编译时还是运行时运行? 我认为一些类似于cfg!()宏将是一个有用的扩展,如果没有其他原因而不是调试。 cfg!()目前已经在编译时进行了评估,所以我认为(/猜测)它应该能够知道该函数是用作 const 还是常规函数,因为这也是在编译时确定的-时间。 然而,这不如我的第一个问题重要。

@Coder-256

  1. 是的,请参阅https://github.com/rust-lang/rfcs/pull/2632。
  2. 我不确定这是否可能,尽管鉴于上述情况,我也不确定是否有必要。

@Coder-256 这两个问题都与 const 泛型无关,而是与 const 函数有关。 常量泛型用于对常量(例如foo<2>() )进行泛型,而不是能够在编译时运行的函数。 我想这就是为什么你没有在 RFC 2000 中找到你的问题的答案。

@rpjohnst谢谢,但我想我可能不清楚。 我已经看过 rust-lang/rfcs#2632 和 rust-lang/rfcs#2000,但我很确定这两者都没有提到。 (但我可能是错的?)我要问的是有条件的常量函数。 请参阅我写的示例,因为它很难描述。

@yodaldevoid哎呀,你说得对,我应该在哪里问这个?

至于宏观问题,我同意现在考虑它并没有太大用处

我要问的是有条件的 const 函数。 请参阅我写的示例,因为它很难描述。

您的square_const定义可以代替square (即:它在运行时被强制转换为具有等效签名的函数)。 有关此行为的讨论,请参阅https://github.com/rust-lang/rfcs/pull/2632 (此外,该线程是询问有关const fn和特征边界之间的任何交互问题的正确位置)。

@varkor我不相信是这种情况(因为特征边界发生了变化),但我会在 rust-lang/rfcs#2632 中询问。

崩溃报告:

代码:

#![feature(const_generics)]

use std::marker::PhantomData;

struct BoundedU32<const LOWER: u32, const UPPER: u32> {
    value: u32,
    _marker: PhantomData<(LOWER, UPPER)>,
}

编译器:

thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.35.0-nightly (719b0d984 2019-03-13) running on x86_64-unknown-linux-gnu

note: compiler flags: -C codegen-units=1 -C debuginfo=2 --crate-type lib

note: some of the compiler flags provided by cargo are hidden

error: Could not compile `playground`.

To learn more, run the command again with --verbose.

@Jezza :const 泛型尚未完全实现,预计不会起作用。 我们将在开始试验#![feature(const_generics)]时发布公告(如果您订阅了此问题,您将收到通知)。

@varkor 等等@Jezza的例子有_marker: PhantomData<(LOWER, UPPER)> - 这是两个const用作类型的参数,为什么rustc_resolve产生错误?

好点:我会调查这个。 请注意,这只是#![feature(const_generics)]的一个问题,所以它不是一个关键问题(使用非通用常量会产生预期的错误)。 也许它永远不会到达rustc_resolve

~ @eddyb :这个 ICE 来自resolve_ident_in_lexical_scope ,所以我想它可能与https://github.com/rust-lang/rust/issues/58307有关。~

编辑:实际上,也许不是——这似乎只适用于macro_rules!

最小化:

#![feature(const_generics)]

struct S<const C: u8>(C);

产生“预期类型,找到值”错误

索引在这里越界:
https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/lib.rs#L3919

当前每晚为以下代码生成“从不使用参数N ”:

struct Foo<const N: usize> { }

从之前的讨论中,我认为 compile 应该接受此代码。 它仅仅是未完成实施的产物吗?

需要使用@newpavlov普通类型参数,应该可以做PhantomData<n>吗?

我想已经可以做到PhantomData<[(); N]> 。 不过,不确定这是我们真正想要强制执行的事情,因为 AFAIU, PhantomData是标记差异,并且 AFAIU 没有差异的概念。 一个 const 泛型参数。

这仅在 N 的类型usize时才有效。

我们确实决定在 RFC 讨论期间没有必要使用 const 参数,并且当前的实现是一个错误。

@withoutboats您能指出 RFC 中所述的位置吗? 我试图找到具有这种效果的东西,但一定是错过了。

我不知道它是否包含在 RFC 文本中

@withoutboats你介意指出讨论的地方吗? 我浏览了 RFC PR,但它并没有引起我的注意。

@yodaldevoid之前提到过此评论: https :

我现在无法浏览几年前的评论历史,但我可以解释:需要使用类型变量作为阻止器来让您考虑这些参数的变化(IMO 这也是不必要的,我们可以默认为协变,但那是一个单独的问题)。 Const 参数与方差没有任何交互作用,因此这将没有动力。

@HadrienG2感谢您找到相关评论。

@withoutboats我真的没想到你会为此浏览所有多年的评论,只是希望你已经掌握了它。

谢谢你的解释。 我必须承认,无论我尝试学习多少次,我都无法围绕方差进行思考,但即使没有这一点,再次考虑不需要使用成本参数也是有道理的。 我会将修复这个错误放到我们的 FIXME 列表中。

这是迄今为止在 const 泛型方面的进展摘要。


在 const 泛型的工作可以正确开始之前,需要进行一些重构。 @jplatte通过https://github.com/rust-lang/rust/pull/45930承担了这项任务@jplatte然后开始致力于const 泛型的主要实现,但发现他们没有足够的时间继续。

@yodaldevoid和我从@jplatte停止的地方开始,但很快发现一般处理泛型参数的方式阻碍了取得进展。 因此开始了一系列更改以改进整个代码库中处理泛型的方式: https : //github.com/rust-lang/rust/pull / 48452https://github.com/rust-lang/rust/pull/48523https://github.com/rust-lang/rust/pull/51880。

完成后,const 泛型的实现就可以认真开始了。 从那以后, @yodaldevoid和我一直在缓慢但肯定地逐渐增加对 const 泛型的支持: https : //github.com/rust-lang/rust /pull/58503 , https://github.com/rust-lang/rust/pull/58581 , https://github.com/rust-lang/rust/pull/58583 , https://github.com/rust -lang/rust/pull/59170https: //github.com/rust-lang/rust/pull/59355、https : //github.com/rust-lang/rust/pull/59415、https ://github .com/rust-lang/rust/pull/60058https://github.com/rust-lang/rust/pull/60280https://github.com/rust-lang/rust/pull/60284和大多数最近https://github.com/rust-lang/rust/pull/59008。 (这些主要是从主要的 const 泛型拉取请求中分离出来


现在是什么状态? 一些const 泛型测试现在可以工作了 🎉然而,仍然有一些不工作,并且在整个代码中还有许多FIXME s 我们仍然需要解决。 不过,我们现在已经接近尾声了,如果您想提供帮助,我们正处于一些唾手可得的果实的阶段。

  • 首先,我们需要更多的测试。 到目前为止,只有少数const 泛型测试,但我们想要更多。 目前,因为我们知道一些问题,所以我们不需要错误报告,但我们确实需要通过测试。 如果您想出一个有效的测试用例(并且看起来与现有测试不太相似),请随时打开拉取请求以添加它。
  • 其次,如前所述,有许多FIXME(const_generics)散布在整个代码中。 我们正计划通过我们的方式解决它们,但是@yodaldevoid和我只有这么多时间,所以如果您认为您可以解决一个问题,请继续(尽管您可能想发表评论,所以我们不会” t 重复努力)。

在 const 泛型准备好进行适当的测试之前,我已经概述了一些剩余的实现问题,在顶帖中,粗略地了解剩下的工作。

这需要时间,但我们一直在稳步取得进展,并希望跟上步伐。 终于看到事情开始到位是令人鼓舞的!

我一直在期待这篇文章@varkor :) 我不是 Rust 巫师,但我想解决FIXME的一个

恭喜@jplatte@yodaldevoid和@varkor。 这是摆脱数学库中自定义Array-like特征的一大步。

交叉发布...

@varkor关于测试,也许有助于了解预期的工作。 例如,我很惊讶这不起作用: https :

另外,供对 FIXME 感兴趣的人参考: https ://oli-obk.github.io/fixmeh/

@标记IM尝试把{}左右FOO 。 这样它就会起作用。

引用https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.md

将表达式作为 const 参数(数组除外)应用时,该表达式不是恒等表达式,该表达式必须包含在块中。 这种语法限制是必要的,以避免在解析类型内部的表达式时需要无限前瞻。

{expression}只有在表达式不是标识符或文字时才应该是必要的,这是一个错误。

引用相同的 RFC:

身份表达式:除非用范围内的名称替换它,否则无法进一步评估的表达式。 这包括所有文字以及所有标识 - 例如 3、“Hello, world”、foo_bar。

@mark-im 是的,目前我们在最初解析时无法区分类型和常量的标识符。 我们将如何处理所有这些问题的决定推到了未来。 我想现在可能是未来。

对于一些背景知识,我们已经讨论了两种我能记住的关于如何处理这个问题的方法。 第一种方法是强制人们标记 const 参数(通过在它们之前键入const或其他方式)。 从人体工程学的角度来看,这不是很好,但从解析的角度来看,这很容易。 第二种方法是将所有泛型参数视为相同,直到我们稍后在编译期间开始将它们与泛型参数配对。 这可能是我们想要采用的方法,并且已经有一些工作可以使这成为可能,只是我们还没有迈出最后一步。

惊人的工作。 如果可以的话,我很乐意通过修复一些FIXME来提供帮助。 我在 rustc 代码库中根本没有太多经验,所以我将从 FIXME 开始https://github.com/rust-lang/rust/blob/master/src/librustc_mir/monomorphize/item.rs# L397 ,因为它看起来很容易。

下面的代码怎么样:

trait Foo {
    const N: usize;
    fn foo() -> [u8; Self::N];
}

目前,它导致“在当前范围内没有为类型Self找到名为N关联项”编译错误。 在完成FIXME之后,这样的代码会被接受还是需要额外的努力来实现?

@yodaldevoid

快速提问,如果已经讨论过,请道歉。

第二种方法是将所有泛型参数视为相同,直到我们稍后在编译期间开始将它们与泛型参数配对。 这可能是我们想要采用的方法,并且已经有一些工作可以使这成为可能,只是我们还没有迈出最后一步。

这是否违背了 Rust 使函数签名显式化以避免产生与函数实现相关的错误的原则? 也许我完全误解不过这和你谈论一个函数体内解析泛型参数。

这是否违背了 Rust 使函数签名显式化以避免产生与函数实现相关的错误的原则? 然而,也许我完全误解了这一点,并且您正在谈论解析函数体内的通用参数。

这是关于确定作为泛型参数传递给函数的标识符是常量还是类型。

例子:

fn greet<const NAME:&'static str>(){
    println!("Hello, {}.",NAME);
}
const HIS_NAME:&'static str="John";
greet::<HIS_NAME>();
greet::<"Dave">();

注意,在定义函数时,必须指定 NAME 是常量,但是在调用它时,不必说 HIS_NAME 是 turbofish 运算符 ( ::< > ) 中的常量。

@zesterer如果此提议的解决方案对用户透明(例如,在定义函数时,参数类型之间没有区别),那么您是正确的。 然而,所提议的解决方案背后的想法不是改变面向用户的行为,而是仅仅改变实现。 用户仍然会写出具有显式类型、常量和生命周期参数的函数签名,并且泛型参数仍然需要匹配相应的参数,只是我们在编译器中解析它的方式会发生变化。

@newpavlov我很惊讶代码不起作用。 如果您可以提交单独的问题,将不胜感激。

@yodaldevoid @robarnold

啊,明白了。 我错误地认为这与类型/函数签名有关。

为了在内置数组类型上使用 const 泛型,我打开了 #60466 以查看其他人对此的意见。

目前,Impl 块似乎完全损坏了。 这在功能的当前状态下是预期的,还是我应该打开另一个问题?

目前,Impl 块似乎完全损坏了。 这在功能的当前状态下是预期的,还是我应该打开另一个问题?

您需要在Nimpl<const N: usize> Dummy<{N}> {}周围添加{} impl<const N: usize> Dummy<{N}> {}

但是,是的,那么您会收到有关不受约束的参数的错误。

啊,谢谢。 忘记了牙套的事情!

一旦解决了它就会失败,我并不感到惊讶,因为这是一个大大最小化的测试用例。

...但是是的,这个应该可以工作:

#![feature(const_generics)]

trait Dummy {}

struct Vector<const N: usize> {
    data: [f32; N],
}

impl<const N: usize> Dummy for Vector<{N}> {}

...它仍然以 E0207 失败:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates
 --> src/lib.rs:9:12
  |
9 | impl<const N: usize> Dummy for Vector<{N}> {}
  |            ^ unconstrained const parameter

error: aborting due to previous error

For more information about this error, try `rustc --explain E0207`.
error: Could not compile `small-matrix`.

To learn more, run the command again with --verbose.

...我认为这就是@varkor在 #60466 中所说的“常量泛型的数组处理问题”的意思:

#![feature(const_generics)]

fn dummy<const N: usize>() -> [f32; N] {
    [0.; N]
}

->

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: cat_expr Errd
 --> src/lib.rs:3:44
  |
3 |   pub fn dummy<const N: usize>() -> [f32; N] {
  |  ____________________________________________^
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: cat_expr Errd
 --> src/lib.rs:4:5
  |
4 |     [0.; N]
  |     ^^^^^^^

error: internal compiler error: QualifyAndPromoteConstants: Mir had errors
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) ("return type"): bad type [type error]
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) (LocalDecl { mutability: Mut, is_user_variable: None, internal: false, is_block_tail: None, ty: [type error], user_ty: UserTypeProjections { contents: [] }, name: None, source_info: SourceInfo { span: src/lib.rs:3:1: 5:2, scope: scope[0] }, visibility_scope: scope[0] }): bad type [type error]
 --> src/lib.rs:3:1
  |
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | |     [0.; N]
5 | | }
  | |_^

thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:356:17
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

error: internal compiler error: unexpected panic

note: the compiler unexpectedly panicked. this is a bug.

note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports

note: rustc 1.36.0-nightly (cfdc84a00 2019-05-07) running on x86_64-unknown-linux-gnu

note: compiler flags: -C debuginfo=2 -C incremental --crate-type lib

note: some of the compiler flags provided by cargo are hidden

@HadrienG2
请参阅 #60619 和 #60632。

我认为这段代码应该构建:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e45b7b5e881732ad80b7015fc2d3795c

但它目前错误:

错误 [E0119]: 类型MyArray<_, _>的 trait std::convert::TryFrom<[type error]>实现冲突:

不幸的是,这是Try{From,Into}的限制并且与 const 泛型没有任何关系。 在许多情况下,它们不能通用: playpen

@oberien在您的示例中, T是外来类型,并且不知道T: Into<Foo<T>>成立。

在我的例子中,[T; N] 是 libcore 中定义的类型,并且是#[fundamental] (我什至不知道这是什么意思),所以我认为特征解析器实际上知道[T; N]: Into<MyArray<T, {N}>>不成立,并且有应该不会冲突吧?

[...] 并且是#[fundamental] [...]

这就是问题所在, fundamental与允许下游用户在将本地类型包装在基本包装类型中时定义特征实现有关。 你可以在这个 Playground 中看到你的实现在非基本类型之间工作,但是如果from类型被标记为fundamental (带有更好的错误消息),则会失败。

@varkor @yodaldevoid所以,我最终遇到了S<{N == 0}>S使用const bool参数和N a const usize ) 在编译器的眼中既不是S<{true}>也不是S<{false}> (因为它没有实现与两者相同的特征),并且不确定这是错误还是当前原型的预期限制。 您能否让我快速回顾一下当前的统一逻辑?

作为一个最小化的例子,这...

// Machinery for type level if-then-else
struct Test<const B: bool>;
trait IfFn<S, Z> { type Out; }
impl<S, Z> IfFn<S, Z> for Test<{true }> { type Out = S; }
impl<S, Z> IfFn<S, Z> for Test<{false}> { type Out = Z; }

// Returns an u8 if B is true, else an u16
fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
    0
}

...产生以下错误:

error[E0277]: the trait bound `Test<B>: IfFn<u8, u16>` is not satisfied
  --> src/lib.rs:32:1
   |
32 | / fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
33 | |     0
34 | | }
   | |_^ the trait `IfFn<u8, u16>` is not implemented for `Test<B>`
   |
   = help: the following implementations were found:
             <Test<false> as IfFn<S, Z>>
             <Test<true> as IfFn<S, Z>>

我实际上不知道,但我认为问题可能在于编译器没有逻辑来决定0文字是u8还是u16 . 尝试类似: 0u8 as _

不是这样,错误信息保持不变。

但是如果你想要一个不涉及整数类型推断的版本,这里有一个更愚蠢的最小化:

struct Test<const B: bool>;
trait Not { const B: bool; }
impl Not for Test<{true }> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }

fn should_be_ok<const B: bool>() -> bool {
    <Test<{B}> as Not>::B
}

由于Test<{B}>据称没有实现Not仍然失败。

你好 ! 我不确定 const 值的统一是否应该起作用,但是有一个新的统一系统(“chalk”)正在开发中,但不是 rustc 当前使用的。
所以你要做什么_可能_必须等到粉笔准备好。 即便如此,我不确定它是否会起作用,或者即使使用粉笔也应该起作用。
有关进展,请参阅https://github.com/rust-lang/rust/issues/48049

@HadrienG2你能

@carado统一是在

@HadrienG2一旦我们专门研究了 const 泛型,你就可以做类似的事情

struct Test<const B: bool>;
trait Not { const B: bool; }
impl<const B: bool> Not for Test<B> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }

fn should_be_ok<const B: bool>() -> bool {
    <Test<{B}> as Not>::B
}

您想要做的是某种详尽的检查,就像将匹配扩展到特征系统一样。 不知道在任何线程中提到它。

@carado Chalk 不关心实际的“类型系统原语”,这是其中之一。
也就是说, rustc仍然必须(即使在今天,因为 Chalk 已经部分集成)实现统一部分,该部分处理对 Chalk 不透明的实体(例如两个不同的常量表达式)。
如果我们实现它(无论如何我们没有任何近期计划),即使在 Chalk 替换特征系统(这是它的主要目的,而不是统一)之后,它也不会发生太大变化。

哦,我的坏话! 我想我真的不知道我在说什么。

const 泛型将提供的令人敬畏的东西之一是编译时计算的简单函数,例如阶乘。

例子:

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<i - 1>().unwrap() + i)
    }
}

据我所知,每晚对 const 泛型的支持有限? 如果是这样,有没有比这个问题更有条理的地方,我可以找到如何使用它和其他东西?

@dancojocaru2000 const函数应该是编译时值级计算的首选方式,例如https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=4994b7ca9cda0bfc44f535944343137不工作,因为他们还没有完全实施。 但是const也不是泛型。

您的代码段可能有问题,因为我不确定match是否应该适用于const参数。 您还在代码中混合了加法和乘法,但这并不重要。

我认为您已经可以在编译时使用 Peano 编码进行阶乘,这通常显示为其他函数式语言的演示。

编译时计算简单函数

我认为更合适的功能是const fn

从理论上讲,您的代码几乎可以按原样运行

#![feature(const_generics)]

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<{X - 1}>().unwrap() + i)
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

但它没有:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/main.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`

尽管您确实应该使用无符号类型:

#![feature(const_generics)]

fn factorial<const X: u32>() -> u32 {
    match X {
        0 => 1,
        1 => 1,
        _ => factorial::<{ X - 1 }>() + X,
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

有没有比这个问题更有条理的地方,我可以找到如何使用它和其他东西?

这通常由不稳定的书涵盖,但现在那里没有任何用处。 当你发现一些东西时,也许你可以考虑开始为此勾画一些内容?

编译时使用 Peano 编码的阶乘,

有关示例,请参阅typenum crate

从理论上讲,您的代码几乎可以按原样运行

当调用factorial::<0>_ => factorial::<{X - 1}>() * X会被编码太对吗? 但是尝试这样做会导致整数下溢。

我可以期待这样的代码在未来编译吗?

#![feature(const_generics)]

trait NeedsDrop<const B: bool> { }
impl<T> NeedsDrop<std::mem::needs_drop<T>()> for T { }

fn foo<D: NeedsDrop<false>>(d: D) { }

这与N <= 32作为常量泛型的一些[T; N]数组的最近实现交叉,但以下代码不会在最新的( 4bb6b4a5e 2019-07-11 )上每晚编译,并出现各种错误the trait `std::array::LengthAtMost32` is not implemented for `[u64; _]`

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
    const N: usize
>(pub [u64; N]) 
where [u64; N]: std::array::LengthAtMost32, 
   [u64; N*2]: std::array::LengthAtMost32;

尽管以下内容确实如此:

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
    const N: usize
>(pub [u64; N]) 
where [u64; N]: std::array::LengthAtMost32;

看起来N*2不是此类约束的有效常量或限定符

@shamatar N*2应该用大括号括起来,如`[u64; {N*2}]

我认为“未实现的特征”错误来自派生宏没有添加[u64; {N*2}]的界限,但如果派生被删除,目前有一个 ICE:

```错误:内部编译器错误:类型中的常量有一个被忽略的错误:TooGeneric
--> src/main.rs:5:1
|
5 | / pub 结构 BigintRepresentation<
6 | | const N:使用
7 | | >(pub [u64; N])
8 | | 其中 [u64; N]: std::array::LengthAtMost32,
9 | | [u64; {N*2}]: std::array::LengthAtMost32;
| |____________________________________________^

线程 'rustc' 因“即使delay_span_bug发出,也没有遇到错误”而恐慌,src/librustc_errors/lib.rs:366:17
注意:使用RUST_BACKTRACE=1环境变量运行以显示回溯。

此代码无法编译:

#![feature(const_generics)]

struct Foo<const X: usize>([u8; X]);

impl<const X: usize> Foo<X> {
    fn new() -> Self {
        Self([0u8; X])
    }
}
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/lib.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error[E0573]: expected type, found const parameter `X`
 --> src/lib.rs:5:26
  |
5 | impl<const X: usize> Foo<X> {
  |                          ^
  |                          |
  |                          not a type
  |                          help: try using the variant's enum: `regex_syntax::ast::HexLiteralKind`

error[E0107]: wrong number of const arguments: expected 1, found 0
 --> src/lib.rs:5:22
  |
5 | impl<const X: usize> Foo<X> {
  |                      ^^^^^^ expected 1 const argument

error[E0107]: wrong number of type arguments: expected 0, found 1
 --> src/lib.rs:5:26
  |
5 | impl<const X: usize> Foo<X> {
  |                          ^ unexpected type argument

error: aborting due to 3 previous errors

@npmccallum你需要做Foo<{X}>

@pengowen123是的,它在没有derive情况下编译(用constant in type had an ignored error: TooGeneric使编译器崩溃),但是这样的“双重约束”是否有效N <= 32可能是一个单独的问题N <= 16不被编译器应用。

提及参数的表达式可能会在一段时间内无法使用, [T; N]Foo<{N}>是特殊情况,其中没有提及N表达式,而是直接引用N参数,绕过更大的问题。

使用Self会导致崩溃。
即使将它换成Value<{C}> ,它仍然崩溃。

#![feature(const_generics)]

struct Value<const C: usize>;

impl<const C: usize> Value<{C}> {
    pub fn new() -> Self {
        unimplemented!()
    }
}

pub fn main() {
    let value = Value::new();
}

您的代码片段不会在游戏围栏上崩溃: https ://play.rust-lang.org/ = nightly = release = 2018 = d3fda06d2e8b3eb739afa99d5da84a33

同#61338,问题根源是增量编译。

编译时计算简单函数

我认为更合适的功能是const fn

从理论上讲,您的代码几乎可以按原样运行

#![feature(const_generics)]

fn factorial<const X: i32>() -> Option<i32> {
    match X {
        i if i < 0 => None,
        0 => Some(1),
        1 => Some(1),
        i => Some(factorial::<{X - 1}>().unwrap() + i)
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

但它没有:

warning: the feature `const_generics` is incomplete and may cause the compiler to crash
 --> src/main.rs:1:12
  |
1 | #![feature(const_generics)]
  |            ^^^^^^^^^^^^^^

error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`

尽管您确实应该使用无符号类型:

#![feature(const_generics)]

fn factorial<const X: u32>() -> u32 {
    match X {
        0 => 1,
        1 => 1,
        _ => factorial::<{ X - 1 }>() + X,
    }
}

fn main() {
    println!("{:?}", factorial::<10>());
}

这个功能对我也很有用, const fn是不够的。 我想将它用于终止条件为维度 = 0 的 N 维数组。

我能够创建一个结构:

#![feature(const_generics)]
struct MyArray<T: Default, const len: usize> {
    real_array: [T; len]
}

impl<T: Default, const len: usize> MyArray<T, {len}> {
    fn new() -> Self {
        return MyArray {
            real_array: [Default::default(); len]
        }
    }
}

这失败了,因为我实际上无法初始化数组,原因是:

error: array lengths can't depend on generic parameters
 --> src/main.rs:9:46
  |
9 |             real_array: [Default::default(); len]
  |                                              

我尝试通过将 const 值设置为通用值来解决它。

这克服了上述错误,但编译器随后崩溃。

error: internal compiler error: src/librustc/ty/subst.rs:597: const parameter `height/#0` (Const { ty: usize, val: Param(height/#0) }/0) out of range when substituting substs=[]

该语言迫切需要基本的常量泛型,这样标准库就不必为每种大小的数组定义每个函数。 这是我不使用 Rust 的唯一原因。 我们真的需要图灵完整的编译时函数吗?
我可能错了,但简单的整数表达式应该足够了,我希望没有人浪费时间,确保这些古怪的例子有效。

该语言迫切需要基本的常量泛型,这样标准库就不必为每种大小的数组定义每个函数

https://github.com/rust-lang/rust/issues/61415已经有一些努力了

我希望没有人在浪费时间,确保这些古怪的例子有效。

有些人这样做,我看不出有什么问题;)

这是我不使用 Rust 的唯一原因。

这是我所看到的对不使用 Rust 的人引用的最不有趣的原因。 你确定你说的是实话?

但简单的整数表达式就足够了

即使缩小范围也非常困难。 试试你的手吧,我们有真正有能力的人在做这个,这也是为什么要花这么长时间的原因。

不幸的是,我最近没能花太多时间来修复 const 泛型错误(或者更普遍的 Rust)。 如果有人想参与推动 const 泛型,我很乐意提供解决未解决问题和审查错误修复的建议,尽管我可能需要一段时间才能再次专注于这一点。

我希望没有人在浪费时间,确保这些古怪的例子有效。

没有人, miri已经比 C++ constexpr强大得多。
没有人在做任何花哨的事情,比如从N + 1 = M + 1派生N = M N + 1 = M + 1

大多数这些错误与它们中的表达式无关,它们与类型系统以及const泛型如何与所有其他功能交互有关。
如果您拥有的只是const泛型和整数文字,它们仍然会存在。

顺便说一句,我认为不需要[expr; N]表达式的“数组长度不能依赖于泛型参数”错误,我们可以使用与[T; N]类型相同的技巧,然后拉取出N而不将其作为表达式求值。

虽然我确实想尝试一下,但我不确定我是不是合适的人。 我很少使用 Rust 并且对编译器理论知之甚少。 我可能需要一些指导,但我当然愿意。 😄

编辑:虽然我在软件方面有相当多的经验。

@varkor ,我一直在寻找对rustc有用的东西,我很乐意站出来帮忙。 另外,感谢您坦诚地说明您分配时间的能力!

@varkor ,我实际上正在跟进上述评论,所以如果您有任何建议,我会

我们现在可以做的一件事,我刚刚意识到允许Foo::<{...}> / [expr; ...]表达式引用通用参数(在...部分)。

这是因为表达式必须嵌套在主体内的某处,这往往会防止循环依赖。

但是,我担心在签名中包含例如[(); [0; 1][0]]会损坏,所以无论如何我们可能需要一个陨石坑运行(现在这些需要永远)。

对于任何有兴趣帮助使用 const 泛型的人,我的建议是查看开放的 const 泛型问题列表,并调查您感兴趣的任何内容。 有些人已经进行了一些调查,这应该在评论中。 大多数问题可能需要一点点挖掘。 如果您打算调查某事,发表评论会很有帮助,因此我们不会重复工作(但如果有一段时间没有任何活动,您通常可以忽略问题受理人)。 如果您有任何问题,可以在问题评论中提出,或者在 Discord 或 Zulip 上提问。 @eddyb,@yodaldevoid,@ OLI-OBK和我所熟悉的许多相关领域和好心人问。 感谢您的关注!

抄送@hameerabbasi@ranweiler

关于 const 泛型的问题:

  1. 我在哪里可以找到文档(这样我就不必在这里提问了)
  2. 目前使用任意类型的const参数存在哪些限制?
  3. const泛型的哪些重要功能未实现/使编译器崩溃?

PS(贡献者:)非常感谢您在此功能上的工作。

const-generics 仍在开发中,所以目前还没有任何官方文档。 我不太确定另外两个问题。 当我上次使用 const-generics 时,当我指定一些 const-generic 参数时,它们崩溃了,但是自从我上次使用它做任何事情已经将近一个月了,所以从那时起事情可能已经发生了变化。

没有人在做任何花哨的事情,比如从N + 1 = M + 1派生N = M N + 1 = M + 1

这对于此类类型约束的求解器非常有用。 例如,当实现两个 N 位数字的加法时,返回大小应为 (N + 1) 以考虑溢出位。 当(例如)给出两个 5 位数字时,求解器应该检查N + 1 = 6 。 希望稍后可以将其固定到 const 泛型上 :)

@flip111是的,我认为计划是稍后添加,但这种通用表达式约束非常复杂且难以实现。 所以我们可能至少在几年内看不到它们。

老实说,解决这些基于约束的问题听起来很像 Prolog 的工作。 也许搭载粉笔引擎是一种选择? 不过,Afaik 它解决了 Traits,对吧?

顺便说一句,我喜欢这个话题,虽然我负担不起这个 atm 的帮助。 感谢所有为 Const 泛型工作的人付出的时间和付出。 💐

一个小更新:我正在 Rust 树上工作。 我确实打算做出贡献,但想自学到不需要过多指导的地方。

我能够创建一个结构:

#![feature(const_generics)]
struct MyArray<T: Default, const len: usize> {
    real_array: [T; len]
}

impl<T: Default, const len: usize> MyArray<T, {len}> {
    fn new() -> Self {
        return MyArray {
            real_array: [Default::default(); len]
        }
    }
}

这失败了,因为我实际上无法初始化数组,原因是:

error: array lengths can't depend on generic parameters
 --> src/main.rs:9:46
  |
9 |             real_array: [Default::default(); len]
  |                                              

我遇到了同样的问题,但找到了使用MaybeUninit的解决方法:
https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=3100d5f7a4efd844954a6fa5e8b8c526
显然,这只是获得正确初始化数组的一种解决方法,但对我而言,这已经足够了,直到有适当的方法可用。
注意:我认为代码应该始终按预期工作,但是如果有人发现使用不安全的错误,我很乐意修复它。

@raidwas当您使用=运算符初始化未初始化的内存时,您正在丢弃未初始化的内存。 改为这样做,

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=5d962ce7c​​553e850030240244608ec00

@KrishnaSannasi谢谢,很好:D(从技术上讲,我没有删除未初始化的内存,因为我只将它用于原语,但在这里有一个正确的版本很好)

从技术上讲,丢弃偶数浮点数是未定义的,但它现在不可利用。

从技术上讲,丢弃偶数浮点数是未定义的,但它现在不可利用。

我原以为删除任何Copy类型,甚至是未初始化的类型,都是无操作的。 也许这值得改变。

从技术上讲,丢弃偶数浮点数是未定义的,但它现在不可利用。

我原以为删除任何Copy类型,甚至是未初始化的类型,都是无操作的。 也许这值得改变。

从技术上讲,它仍然是UB。 虽然安全地删除定义的Copy值是一个空操作,但如果您尝试删除任何类型的未初始化内存(例如删除所有可能触及该值的代码),编译器可能会决定进行一些意外优化,这可能会破坏东西。 这是我对它的理解。

不客气,但 ≥ 60 人会收到有关此线程评论的通知,因此我们可能应该将偏离主题的讨论保持在最低限度。 让我们宁愿使用它来协调 const 泛型的工作,因为这是我们都希望发生的事情。

如果我使用 _aliases 指向带有 const-param_ 来自另一个板条箱(依赖项)的类型,则会崩溃。

例如:

  • 板条箱 A,lib
    ```#![crate_type = "lib"]

    ![功能(const_generics)]

酒吧类型别名= 结构;
pub struct 结构体(T);

- crate B

extern crate crate_a;
使用 crate_a::Alias;

pub fn inner_fn(v: 别名) {}
``
崩溃日志 (44580).txt

@fzzr- 听起来像#64730

我一直在将一些代码切换到 const 泛型,看起来确实有两种不同的用例。 我不确定是否应该将它们混为一谈,或者我们是否会更好地为用例使用两种不同的语法。

我的很多用法实际上并不是针对常量值在确定类型方面发挥作用的类型,而是利用为非常量/文字值锁定的功能(仍然不完全支持,例如匹配模式,但最终将需要)。

恕我直言,我们应该正式将“const 参数”与 const 泛型放在一起,这样人们就不会编写引入一千个“const 泛型”(每个参数一个)的变异代码来让编译器将某些变量评估为文字/常量。

@mqudsi你能举个例子吗? 已经有计划和正在进行的基础工作正在进行,以使 const eval 更强大。 不过,这与 const 泛型是正交的。

我的意思是,如果你想重构典型的解析器代码,如下所示以供重用:

let mut result: u32 = 0;
let mut digits = 0;
while digits < max_digits && src.has_remaining() {
    match src.get_u8() {
        d<strong i="6">@b</strong>'0'..=b'9' => {
            result = result*10 + (d - b'0') as u32;
            digits += 1;
        },
        b'>' => match result {
            0..=191 => break, // valid range
            _ => return Err(DecoderError::OutOfRange),
        },
        _ => return Err(DecoderError::Malformed)
    }
}

您通常会将它移到一个函数中,除非以下内容不起作用,因为模式匹配中使用的某些值已成为变量:

#[inline(always)]
fn read_str_digits<B: bytes::buf::Buf>(src: &mut B, stop_word: u8, 
    max_digits: u8, min_value: u32, max_value: u32) -> Result<u32, DecoderError> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="10">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            stop_word => match result {
                min_value..=max_value => break, // valid range
                _ => return Err(DecoderError::OutOfRange),
            },
            _ => return Err(DecoderError::Malformed)
        }
    }

    ...
}

如果 const 泛型出现在 const 参数之前,我可以突然滥用 const 泛型来提出以下内容:

#[inline(always)]
fn read_str_digits<const MinValue: u32, const MaxValue: u32, const StopWord: u8, B: bytes::buf::Buf>
(src: &mut B, max_digits: u8) -> Result<u32, DecoderError> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="14">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            StopWord => match result {
                MinValue..=MaxValue => break, // valid range
                _ => return Err(DecoderError::OutOfRange),
            },
            _ => return Err(DecoderError::Malformed)
        }
    }

    ...
}

到今天为止,即使是这段代码也无法工作,因为编译器没有将 const 泛型值检测为在模式中有效的常量,但这显然是不正确的,需要在 RFC 2000 落地之前解决。 不要误会我的意思,我多年来一直在为通用常量而战,而且我已经准备好 PR 准备去打十几个主要的板条箱(我迫不及待地让chrono的时区成为一个常量通用和统一所有各种DateTime类型),但是恕我直言,如果有可能滥用 const 泛型来伪造 const 参数(其中“const”我们真正的意思是“文字”),那么你会看到广泛的滥用那个。

这并不一定是世界末日,但无需挖太深了进去,它看起来好像一个正确和完整的常量泛型实施,将一定包括常量参数所有的管道,无论如何,所以我们不妨采取额外的时间在我们处理时完成 const 参数的语法/ux/story 并避免代码臭味的不幸时代。 是的,上述仍然可以用宏来完成,但是 const 泛型的人体工程学要简单一千倍。

fwiw,这就是我想象的 const 参数版本的样子:

#[inline(always)]
fn read_str_digits<B: Bytes>(src: &mut B, min_value: const u32, 
    max_value: const u32, stop_word: const u8, max_digits: u8) -> Result<u32, ()> {
    let mut result: u32 = 0;
    let mut digits = 0;
    while digits < max_digits && src.has_remaining() {
        match src.get_u8() {
            d<strong i="23">@b</strong>'0'..=b'9' => {
                result = result*10 + (d - b'0') as u32;
                digits += 1;
            },
            stop_word => match result {
                min_value..=max_value => break, // valid range
                _ => return Err(()),
            },
            _ => return Err(())
        }
    }

    ...
}

除了语义之外,它实际上与 const 泛型相同。

您通常会将它移到一个函数中,除非以下内容不起作用,因为模式匹配中使用的某些值已成为变量:

这似乎是一个用例,通过允许在模式(不仅仅是常量)中使用绑定变量的值,使用一些新语法更容易解决。 目前在模式中允许使用常量这一事实是违反直觉的,据我所知,这是历史性的。

@mqudsi如果这是一个愚蠢的问题,请原谅我,但我认为您提供的示例没有任何问题。 对我来说,它看起来像是 const 泛型的一个完全有效的用例:具有一个泛化的定义,可以将任意值用作最大值/最小值。 我并没有真正看到 const 参数相对于 const 泛型的好处。 他们似乎和我一样; 也就是说,const 参数可以作为对 const 泛型的脱糖来实现。 您能否详细说明这种设计模式有什么问题?

这是自上次更新以来关于 const 泛型的工作总结。 上一次,一切都与核心实现有关:确保所有东西都安装在一起并让一些基本的测试用例通过。 从那时起,努力的重点是使 const 泛型更可靠:修复导致编译器崩溃或无法意外工作的情况,或改进诊断。 尽管还有很长的路要走,但我们离可靠工作的东西越来越近了。


  • @skinny121在上个月做了出色的工作,修复了 const 泛型的各种突出问题:

    • 扩展测试套件 (https://github.com/rust-lang/rust/pull/60550)

    • 修复类型推断问题(https://github.com/rust-lang/rust/pull/64679,https://github.com/rust-lang/rust/pull/65579)

    • 在 const 泛型中支持更多类型,包括字符串和切片(https://github.com/rust-lang/rust/pull/64858)和指针(https://github.com/rust-lang/rust/pull/64986) )(但请注意,这些可能不会立即与其余 const 泛型一起稳定下来,因为原始 RFC不支持它们)

    • 使用 const 泛型修复诊断问题(https://github.com/rust-lang/rust/pull/65154,https://github.com/rust-lang/rust/pull/65579)

    • 修复了使用 const 泛型跨板条箱的一些问题 (https://github.com/rust-lang/rust/pull/65365)

  • @eddyb修复了一些影响 const 泛型的 const 评估问题(https://github.com/rust-lang/rust/pull/63497)
  • @matthewjasper修复了在宏中使用 const 泛型的一些问题(https://github.com/rust-lang/rust/pull/63083)
  • @davidtwco修复了一个涉及 const 泛型和构造函数的问题(https://github.com/rust-lang/rust/pull/60839)
  • @GuillaumeGomez修复了
  • @varkor修复了一些类型推断问题(https://github.com/rust-lang/rust/pull/61570、https://github.com/rust-lang/rust/pull/60742、https://github。 com/rust-lang/rust/pull/60508),限制可以使用 const 泛型的地方的问题 (https://github.com/rust-lang/rust/pull/60717) 和各种编译器崩溃 (https:/ /github.com/rust-lang/rust/pull/61380, https://github.com/rust-lang/rust/pull/61333, https://github.com/rust-lang/rust/pull/60710 )

此外,编译器上的其他工作最终解决了一些与 const 泛型相关的其他问题。

我们也开始在编译器内部使用 const 泛型:由于@crlf0710@scottmcm (https://github.com/rust-lang/rust/pull/60466,https:https://github.com/rust-lang/rust/pull/60466, https ://github.com/rust-lang/rust/pull/62435),这导致了性能改进,并且还允许我们在未来(当 const 泛型稳定时)不限制数组特征的实现@Centril使用相同的方法来改进VecDeque (https://github.com/rust-lang/rust/pull/63061)。


在过去的几个月里,const 泛型的许多特别常见的错误已经得到修复。 @nikomatsakis正在研究惰性归一化,这应该可以解决许多剩余的问题,而@jplatte正在研究修复const 参数与类型参数的{X} const 参数)。

我还要感谢@eddyb在整个开发过程中对 const 泛型的所有指导和审查,这非常宝贵。

还有很多其他问题需要解决,和以前一样,我们可以使用我们可以获得的所有帮助 - 如果您有兴趣解决任何剩余的问题,请随时就该问题与我联系,或者Discord 或 Zulip 获取入门指南。

@mqudsi @mark-im 将泛型的当前语法替代方案(参数位置中的impl Trait扩展到 const 泛型是否合适? 这是为此用例建议的语法@mqudsi

我个人认为这会提高此类用例的可读性,但我看到一个问题:普通泛型用于传递数据,因此它们绑定到一个参数。 这不是 const 泛型的情况,那么您将如何传递它们? 假装它们是参数?
在参数位置impl Trait的情况下,它也不能与 turbofish 语法一起使用。 对于参数中的const ,它将是相反的。 这可能令人困惑。

我不确定这里的好处是否大于“怪异”,但无论如何我想分享我的想法。

依稀想起之前关于这个的讨论,现在找到了: https :

@PvdBerg1998这似乎是一个非常糟糕的主意。 我真的不太明白<…>是一种难以阅读的 _hard 语法_。 这里有两种可能:

  1. 你和我一样认为它不难阅读,即使有几个界限。
  2. 您认为这很难阅读:那么也许解决方案是将它们放在where子句中?

现在与上面链接的pre-RFC进行比较。 它将在函数参数的位置直接引入 _tag_(在类型系统的意义上),即函数体的变量绑定。

换句话说:它非常令人困惑,并且正如一些人在 arg 位置为 impl Trait 所说的那样,它是不需要的。 请不要两次犯同样的错误来添加不值得的功能。 特别是如果你“假装他们是争论”。 Rust 在这里会走向错误的方向。 语法应该更简洁,是的,但不要更混乱。 请停下。

@phaazon
我个人完全同意链接的提案,即在参数位置允许常量将是一个非常好的人体工程学提升。 这不仅与函数签名有关(可以说where子句根本不能“解决”问题,而且会使签名更难理解,因为您必须解析 3 个位置而不是一个),而且还与如何使用这些功能。 相比:

let r = _mm_blend_ps::<{2 * C}>(a, b);
let r = _mm_blend_ps(a, b, 2 * C);

在我看来,第二行比第二行更自然。 另一个例子是矩阵创建: MatrixF32::new(3, 3)MatrixF32::new::<3, 3>() 。 我强烈不同意此功能将“非常混乱”。 对于用户来说,这只不过是对函数参数的附加要求。 我们已经可以通过宏模拟 const 参数(请参阅std SIMD 内在函数),但它非常丑陋且效率低下。

关于参数位置的impl Trait ,最初我也反对这个功能,但一段时间后我改变了主意,我相信最终这是一个很好的决定。 现在唯一不完整的部分是与通过 turbofish 提供的显式类型参数的交互。 我认为一个好的解决方案是让impl Trait参数对 turbofish 不可见,即用户应该在他们确定不需要显式类型消歧时使用它。 在某些情况下,这将使我们能够显着减少 turbofish 内部对_需求。

我想这里对 const 参数的讨论是题外话,所以可能不应该在这里继续。

const参数与impl Trait参数( @PvdBerg1998提到的)分开的一件事是它们参数,所有这些都意味着类型推断。

您不能将类型本身作为参数传递,但您可以传递一个值,并且让函数从其正常的带括号的参数推断其 const 泛型参数是完全合理的。

这在 C++ 中很常见,例如:

template <typename T, size_t N>
size_t length(T (&array)[N]) {
    return N;
}

微妙的不同从简单地接受一个const参数并将其脱糖对const通用参数( fn foo(const N: usize) - > fn foo<const N: usize>() )。 它更接近于使用 const 泛型参数来约束参数,然后从该参数中推断出该 const 泛型参数。 所以上面的矩阵示例可能如下所示:

impl<const W: usize, const H: usize> MatrixF32<W, H> {
    fn new(w: usize<W>, h: usize<H>) -> Self { .. }
}

...其中usize<X>是捏造的语法“一usize与价值X ”。 (类似地,您可能需要usize<X..Y>用于范围类型,这已在泛化NonZero的上下文中讨论过。)

如果您想避免在范围类型中使用绳索,您可以查看依赖类型语言,它允许直接在类型中使用参数,并进行一些范围调整:

fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }

它们不是参数。 它们是泛型。 在类型系统级别,这意味着它们的 _values_ 存在于编译时,而函数参数的值存在于运行时。 将两者混用是极其误导的。

因为我关心类型系统,所以这个提议违背了很多理智和合理的原则。 您不想混合使用值和类型。 因为,是的,一个N (注意我没有说usize ,我说的是N ),即使你认为它是一个值,更类似于一个类型比一个值。 作为一个基本原理,Haskell 没有 const 泛型,但它有DataKinds ,允许将常规数据构造函数提升为类型:

foo :: Integer -> Integer
foo 0 -- 0 has type Integer

-- but
data P (a :: Integer)

type MyP = P 10 -- 10 has kind Integer, which “value” is the 10 type

所以:

fn foo<const X: usize>()

在这里, X更类似于类型而不是值。

另外,我关心单态化。 如果我读到这个:

let x = foo(12, "Hello, world!", None);

根据你的提议,如果我们查看foo的定义,很难知道哪些参数会统一foo 。 因为每次为 const 泛型传递不同的值时,都会创建一个完整的新函数。

也许_你_和_你的原因_感觉更直观,但我也有理由说它在类型正确性方面根本不直观。 声明它们是参数就像声明参数化函数,在数学中,有它们的参数参数。 你混淆了参数和参数,证明了这个想法是多么糟糕。

你可能误读了我,我特别说“ const参数......是参数”,而不是“ const泛型”。 我的第一个 Rust 示例也完全等同于 DataKinds( usize<N>N的类型级版本)。 就类型正确性而言,这里没有问题 - 我不是根据直觉进行争论,而是通过类比现有的、已建立的和健全的类型系统。

至于你对单态化的担忧——这与今天没有什么不同! 每个参数都可能导致新的单态化。 我还要补充一点,这个问题的解决方案是通过在实例之间共享非依赖代码来降低所有泛型的成本,尤其是通过在可能的情况下将 const 泛型转换为运行时值。

(而且我对您声称我混淆了参数和参数的说法感到非常困惑,我很小心地将它们区分开来。)

我明白你的意思,但我仍然感到非常担心。 因为 _argument_ 被标记为 _const_,这意味着你不能传递它 _a value_,从运行时来说。 您需要向它传递一个 _constant value_,它将映射到 _const generic_。 顺便说一下,您在这里没有提到它,但如果您将10视为一种类型,那只是 _singleton type_ 的应用:它的单个可能值是10

因为我一直在 arg 位置反对 impl Trait,所以我也反对那个(你会明白为什么我们可以在这里进行比较)。 几点:

  1. 我认为没有必要同时 _const generics_ 添加很多值。
  2. 由于它们类似于类型,它们应该属于类型所在的位置,而不是变量所在的位置。
  3. 我真的很想知道为什么有些人认为<…>(…)更难,尤其是考虑到structfn 。 目前struct只有<…>fn都有,因为它们都可以设置参数。
  4. 我真正不喜欢在 arg 位置使用 impl Trait 的事实是,很难看到函数真正设置参数的事实。 当您阅读foo<A, B> ,您就会知道有两个参数会产生函数定义和实现。

我觉得你想在这里做什么。 foo(1, 3)对你来说比foo<3>(1) foo(1, 3)感觉更好,但对我来说不是。 这样做的原因是foo(1, 3)应该接受基于运行时的第二个参数,而您给出的建议禁止它。 不过,我可以看到应用程序,尤其是数组,但我目前不喜欢它的样子。 对于“如果一个参数是const ,我们可以推断出一个类型变量/const generic。

fn foo<const N: usize>(a: usize, b: usize | N);

这只是我想出来的假想语法来解释我的观点:如果你将b作为常量值传递给N = b并且你最终会得到你漂亮的语法:

foo(1, 3)

如果b不是 const 值,则需要显式传递N

foo<3>(1, x)

另外,根据您的建议,此操作的语法是什么:

fn foo<const N: usize>(x: [f32; N]);
fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }

这就引出了关联项的问题,简而言之, new()是关联函数,必须与具体类型关联,const 泛型项的语义类似于“This type has this param...”,即依赖类型。 因此,上下文中的new()不应该有任何参数。 显式用法应该类似于Type<32>::new() 。 当然,所有 const 泛型参数都应该是可省略的。

我觉得你想在这里做什么。 foo(1, 3) 对你来说比 foo<3>(1) 感觉更好,但对我来说不是。 这样做的原因是 foo(1, 3) 应该接受基于运行时的第二个参数,而您给出的建议禁止它。

这不是我的动机或意图——我不是在谈论什么“感觉更好”,我不认为编译/运行时的区别需要与类型/值级别的区别混为一谈。 两者已经是分开的 - const项目(值或 fn)当然不是类型!

我所得到的只是类型推断在值级参数和类型级参数之间绘制的语义联系。 与impl Trait没有关系 - 我的观点再次是const 参数与impl Trait区分开来,正是为了避免这种无效率的焦虑。

另外,根据您的建议,此操作的语法是什么:

fn foo<const N: usize>(x: [f32; N]);

那个语法不会改变! 我不是建议我们从类型级别(语法语义上)删除 const 参数,只是我们将它们添加到值级别,部分是为了帮助推断类型级别的参数。

在 cppcon 上有一个关于 constexpr 函数参数的讨论,这似乎与@PvdBerg1998所谈论的相似,并且看起来就像@mark-im 链接到的内部线程。 它似乎在那里很受欢迎。

这似乎是个好主意,但应该在我们完成 const-generics 的初始实现之后制定出来

CppCon 2019:David Stone - 从 C++ 中移除元编程,N 的第 1 部分:constexpr 函数参数

编辑:该线程中提到了这一点,这是 constexpr 函数参数的相关论文
https://github.com/davidstone/isocpp/blob/master/constexpr-parameters.md

我想请求我们保留对内部线程或 RFC 存储库中的问题(或修改 PR)的如此重要的句法更改的讨论。 这个问题的评论部分已经很长了,有问题的语法对于 const 泛型的可用 MVP 似乎是不必要的。

如果我们可以移动评论,我们可能会。 我们可能想将问题锁定给@rust-lang 团队成员并隐藏所有离题评论?

所有设计讨论都应该在 RFC repo 上进行,并且所有错误报告都应该在单独的问题中。

cc @rust-lang/moderation 这样做有先例吗?

您可以将评论_转换_到问题,但不能移动评论。 锁定很好,我只是隐藏主题评论。

随着新的一年的开始,我认为最好再介绍一次我们现在使用 const 泛型的情况。 自从const 泛型 RFC被接受以来,已经有 2 年多了。 在第一年(大约 2018 年),进行了一系列大规模的重构工作,通过改进整个编译器对泛型参数的处理来促进 const 泛型。 在第二年(大约 2019 年),开始着手实现 const 泛型本身:首先是获得最低限度的工作,然后放慢提高功能完整性的速度。 人们也开始尝试在编译器本身中使用 const 泛型。 前两个更新帖子中对这些工作进行了更详细的描述: [1][2]


自上次更新以来,过去几个月取得了良好的进展。

  • @skinny121

    • 修复了在增量编译和跨 crate 下破坏 const 泛型的问题 (https://github.com/rust-lang/rust/pull/65652)

    • 修复了类型推断影响诊断中的常量泛型的错误 (https://github.com/rust-lang/rust/pull/65579)

    • 重构了 const 评估的接口(https://github.com/rust-lang/rust/pull/66877)

  • @yodaldevoid实现了 type 和 const 参数的消歧,以便 const 参数不再需要包裹在{} (https://github.com/rust-lang/rust/pull/66104)
  • @eddyb修复了使用 const 泛型作为数组长度的问题,一旦实现了惰性规范化(https://github.com/rust-lang/rust/pull/66883)
  • @varkor

    • 实现了structural_match检查以禁止使用自定义相等检查的类型作为常量泛型(https://github.com/rust-lang/rust/pull/65627)

    • 更正了一些诊断以将常量泛型考虑在内(https://github.com/rust-lang/rust/pull/65614)

    • 执行了各种重构和类型推断修复(https://github.com/rust-lang/rust/pull/65643、https://github.com/rust-lang/rust/pull/65660、https://github。 com/rust-lang/rust/pull/65696)

非常感谢所有帮助使用 const 泛型的人!


下一步是什么? 目前 const 泛型的最大障碍是惰性归一化,这是某些类型的 const 泛型边界(例如在数组中)所必需的。 @skinny121目前正在研究惰性规范化,继续他们出色的努力,以消除const 泛型中的大错误。 这将解决目前 const 泛型的许多问题。

除了懒惰的规范化,我们仍然有大量的错误和剪纸需要解决。 如果您有兴趣解决任何剩余的问题,请随时在此问题上与我联系,或者在 Discord 或 Zulip 上获取有关入门的指导。 我相信我们可以在 2020 年取得良好进展,并希望接近稳定成为可行讨论的地步!

是否有一个 const 泛型的子集不会遇到惰性规范化并且没有很多错误和剪纸,但是可以表达大量有用的代码? 我注意到 std 对数组的许多特征的实现似乎工作得很好。 也许有一个缩小范围,允许其他 crate 为他们自己的特征编写我们在 std 中拥有的那种实现,即使它们不支持所有更高级的功能?

我们现在已经过半年了,所以我将简要总结一下正在进行的工作。 在过去的 6 个月里,有很多人参与了改进 const 泛型支持,所以感谢所有以某种方式提供帮助的人! 我特别要感谢@skinny121@lcnr ,他们都解决了const 泛型长期以来一直缺乏的一些大特性:常量的惰性规范化方法调用中的const 参数支持,以及修复了许多困难的错误。 从下面的总结中可以明显看出他们的努力。

Const 泛型现在在整个编译器的多个地方使用,并且已经有使用 const 泛型的特征和函数的实验,例如slice::array_chunksIntoIteratorFromIterator用于[T; N] (https://github.com/rust-lang/rust/pull/65819,https://github.com/rust-lang/rust/pull/69985)。 数组特征实现的长度为 32 的限制也终于准备好移除了

我们目前正在讨论稳定const 泛型的一个子集应该涵盖广泛的用例(尽管在 const 泛型完全稳定之前仍有一些问题需要解决)。


  • @skinny121

    • 实现了懒惰规范化的第一个版本(https://github.com/rust-lang/rust/pull/68505, https://github.com/rust-lang/rust/pull/69181, https://github. com/rust-lang/rust/pull/67717, https://github.com/rust-lang/rust/pull/67890)

    • 改进的性能 (https://github.com/rust-lang/rust/pull/68118)

    • 修复了各种错误(https://github.com/rust-lang/rust/pull/68143)

  • @lcnr :

    • 在方法的类型参数中添加了对显式常量泛型的支持 (https://github.com/rust-lang/rust/pull/74113)

    • 用@skinny121 完成了常量的惰性归一化的第一个实现(https://github.com/rust-lang/rust/pull/71973)

    • unused_braces添加了 lint (https://github.com/rust-lang/rust/pull/70081)

    • 修复了模式中 const 泛型的问题(https://github.com/rust-lang/rust/pull/70562)

    • 禁止dyn Trait用于 const 泛型参数 (https://github.com/rust-lang/rust/pull/71038)

    • 改进的漂亮打印 (https://github.com/rust-lang/rust/pull/72052)

    • 修复了各种错误(https://github.com/rust-lang/rust/pull/70032, https://github.com/rust-lang/rust/pull/70107, https://github.com/rust- lang/rust/pull/70223、https://github.com/rust-lang/rust/pull/70249、https://github.com/rust-lang/rust/pull/70284、https://github。 com/rust-lang/rust/pull/70319, https://github.com/rust-lang/rust/pull/70541, https://github.com/rust-lang/rust/pull/72066, https: //github.com/rust-lang/rust/pull/74159)

    • 重构 (https://github.com/rust-lang/rust/pull/70478)

    • 添加了测试(https://github.com/rust-lang/rust/pull/74392,https://github.com/rust-lang/rust/pull/74445)

  • @eddyb

    • 修复了数组长度中 const 泛型的问题(https://github.com/rust-lang/rust/pull/70452)

    • 改进的漂亮打印 (https://github.com/rust-lang/rust/pull/71232)

    • 改进的错误处理(https://github.com/rust-lang/rust/pull/71049)

    • 重构 (https://github.com/rust-lang/rust/pull/70164)

  • @Aaron1011 :修复了跨板条箱常量泛型使用的错误(https://github.com/rust-lang/rust/pull/72600)
  • @petrochenkov :修复了名称解析和常量泛型的问题(https://github.com/rust-lang/rust/pull/70006)
  • @yodaldevoid :修复了 const 泛型参数中生命周期使用的问题(https://github.com/rust-lang/rust/pull/74051)
  • @contrun :修复了一个 ICE (https://github.com/rust-lang/rust/pull/69859)
  • @oli-obk:重构(https://github.com/rust-lang/rust/pull/69981)
  • @Centril :改进的诊断(https://github.com/rust-lang/rust/pull/70261)
  • @DutchGhost :添加了一个测试(https://github.com/rust-lang/rust/pull/70539)
  • @varkor

    • 改进的漂亮打印 (https://github.com/rust-lang/rust/pull/67909, https://github.com/rust-lang/rust/pull/68111)

    • 修复了各种错误(https://github.com/rust-lang/rust/pull/67906, https://github.com/rust-lang/rust/pull/68388, https://github.com/rust-朗/锈/拉/68434)

    • 改进的诊断(https://github.com/rust-lang/rust/pull/70845)

    • 添加了测试(https://github.com/rust-lang/rust/pull/68312, https://github.com/rust-lang/rust/pull/70939)


虽然 const 泛型已经走了很长一段路,但仍然存在错误和尖锐的边缘。 如果您有兴趣解决任何剩余的问题,请随时就该问题或@lcnr@eddyb联系,以获取有关入门的指导。 谢谢!

此页面是否有帮助?
0 / 5 - 0 等级