rust 为什么编译器不使用const注解就不能检测到函数是常量呢?

kfgdxczn  于 2022-12-29  发布在  其他
关注(0)|答案(3)|浏览(141)

在Rust中,const函数在可以放入哪些代码方面受到很大限制,例如,for循环是不允许的,任何非const函数调用也是不允许的。我知道const函数中存在堆分配问题,但为什么下面的代码是无效的呢?

fn add(a: u8, b: u8) -> u8 {
    a + b
}
const A: u8 = add(1, 2);

如果add被声明为const函数,则这是有效的。

  • 为什么Rust编译器不能检测到这是有效的?
  • 为什么const函数是必需的呢?
  • 为什么for循环都不允许在它们内部使用(尽管while loop是允许的)?
3hvapo4f

3hvapo4f1#

为什么const函数是必需的呢?
常量函数声明为const,以告知编译器允许从const上下文调用此函数。常量求值在名为miri的工具中完成。Miri不能计算所有表达式。因此const函数非常有限。有一个tracking issue的扩展已经被接受,并且有积极的工作正在进行,使他们常量兼容。
为什么Rust编译器不能检测到这是有效的?
虽然技术上可行,但Rust团队还是决定不这么做。主要原因是语义版本问题。更改函数体而不更改签名,可能会使函数不再与常量兼容,并破坏常量上下文中的使用。const fn是一个契约,破坏该契约就是显式破坏性更改。
为什么连for循环都不允许在它们内部使用(尽管while循环可以)?
至于为什么for循环不被允许,从技术上讲是允许的。唯一的问题是Iterator::next方法不是const,这意味着不是一些迭代器可以执行非常量求值的操作。直到一种语言构造允许trait方法被标记为const,或者能够放置const * bounds *,你将不得不坚持使用常规的loop s。

const fn looop(n: usize) {
    let i = 0;
    loop {
        if i == n { return; }
        // ...
    }
}
rsaldnfx

rsaldnfx2#

注解const函数的原因与注解pub函数的原因相同:如果您正在编写一个const函数,您可能知道它并且是有意这样做的(因为,正如您所说的,const函数的功能非常有限)。
1.向所有调用者清楚地传达这是一个const函数,
1.如果您不小心在编译器中执行了一些非const的操作,则允许编译器对此进行验证,而不是默默地让它通过,并且
1.如果有人错误地认为某个函数是或不是const,请保证将来对此函数实现的更改不会导致下游问题。
至于for循环,它们并不是我们的目的,如果你想在编译时生成代码,我们有一个完整的子系统:const fn是一个短函数,可以在运行时上下文中使用,但允许在编译时对运行进行额外的优化。(可能终止也可能不终止)增加了很多复杂性,并且可能导致编译器无法终止。有很多无限迭代器,判断迭代对象是否是无限的等价于停止问题,所以保持简单并且不允许这样做更容易。

new9mtju

new9mtju3#

为什么Rust编译器不能检测到这是有效的?
它可以。它 * 不会 * 的原因是删除const是一个向后不兼容的更改,因此必须选择加入:作为一个开发者,通过发布一个const函数,你对用户做出了一个非常具体的承诺。让一个函数的常量根据它的实现细节自动改变而不发出警告是很糟糕的。
这与Copy没有尽快自动实现的原因非常相似:删除Copy会破坏代码,因此Copy需要成为选择的保证,虽然它在技术上是微不足道的更改,但它绝对不是一个"无害"的更改。
为什么const函数是必需的呢?
因为替代方案(像Zig的comptime或C++的模板)并不真正符合Rust的安全哲学,以及它受Elm启发的提供一些有用的编译器错误的愿望。
为什么连for循环都不允许在它们内部使用(尽管while循环可以)?
你有没有考虑过看一下?有一个RFC告诉你:https://rust-lang.github.io/rfcs/2344-const-looping.html
for循环在技术上也是允许的,但不能在实践中使用,因为每次迭代都调用iterator.next(),而iterator.next()不是const fn,因此不能在常量中调用,未来的RFC(如https://github.com/rust-lang/rfcs/pull/2237)可能会取消这个限制。

相关问题