【问题标题】:What is the difference between a constant and a static variable and which should I choose?常量和静态变量有什么区别,我应该选择哪个?
【发布时间】:2019-03-16 00:52:33
【问题描述】:

我从RFC 246知道这个:

  • 常量声明常量值。这些代表一个值,而不是内存地址。这是人们最常使用的东西,并且几乎在所有情况下都会取代我们今天所知的static
  • 静态声明全局变量。这些代表一个内存地址。它们很少使用:主要用例是全局锁、全局原子计数器以及与旧版 C 库的接口。

当我尝试维护一个表时,我不知道两者之间实际上有什么不同。

我应该选择哪一个?

【问题讨论】:

标签: variables rust static constants


【解决方案1】:

可变性

Rust 中的 constant 是不可变的。您既不能重新分配也不能修改它:

struct Foo(u32);

const FOO: Foo = Foo(5);
const mut FOO: Foo = Foo(5); // illegal

fn main() {
    FOO = Foo(1); //illegal
    FOO.0 = 2; //illegal
}

static 变量可以是可变的,因此可以修改或重新分配。请注意,编写/修改全局 static 变量是不安全的,因此需要 unsafe 块:

struct Foo(u32);
static FOO: Foo = Foo(5);
static mut FOO_MUT: Foo = Foo(3);

fn main() {
    unsafe {
        FOO = Foo(1); //illegal
        FOO.0 = 2; //illegal

        FOO_MUT = Foo(1);
        FOO_MUT.0 = 2;
    }
}

发生次数

当您编译二进制文件时,所有const“出现”(您在源代码中使用const)将直接替换为该值。

statics 将在您的二进制文件中有一个专门的部分用于放置它们(BSS 部分,请参阅Where are static variables stored in C and C++? 了解更多信息)。


总而言之,请尽可能使用const。当不可能时,因为您需要在程序中稍后使用非const 方法初始化变量,请使用lazy_static!

内部可变性

虽然conststatic 都可以使用内部可变性,但您永远不应该使用 const。这是一个例子

use std::sync::atomic::{AtomicU32, Ordering};

static STATIC: AtomicU32 = AtomicU32::new(0);
const CONST: AtomicU32 = AtomicU32::new(0);

fn print() {
    println!("static: {}", STATIC.load(Ordering::Relaxed));
    println!("const:  {}", CONST.load(Ordering::Relaxed));
}

fn main() {
    STATIC.store(3, Ordering::Relaxed);
    CONST.store(3, Ordering::Relaxed);

    print();
}

这编译得很好,没有任何警告,但会导致不需要的行为。输出:

static: 3
const:  0

使用clippy时,会显示以下两个警告:

warning: a `const` item should never be interior mutable
 --> src/main.rs:4:1
  |
4 | const CONST: AtomicU32 = AtomicU32::new(0);
  | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  | |
  | make this a static item (maybe with lazy_static)
  |
  = note: `#[warn(clippy::declare_interior_mutable_const)]` on by default
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const

warning: a `const` item with interior mutability should not be borrowed
 --> src/main.rs:8:27
  |
8 |     println!("const: {}", CONST.load(Ordering::Relaxed));
  |                           ^^^^^
  |
  = note: `#[warn(clippy::borrow_interior_mutable_const)]` on by default
  = help: assign this const to a local or static variable, and use the variable here
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const

warning: a `const` item with interior mutability should not be borrowed
  --> src/main.rs:13:5
   |
13 |     CONST.store(3, Ordering::Relaxed);
   |     ^^^^^
   |
   = help: assign this const to a local or static variable, and use the variable here
   = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#borrow_interior_mutable_const

【讨论】:

  • @TimDiekmann 虽然我同意您的建议精神,但我认为这太高级了。在某些情况下,const工作,但与static 相比性能很差,例如the code that OP links to
  • @TimDiekmann 当然,但是您在 cmets 中引用了它,因此似乎同意该声明的真实性并同意该指导。你好会在他们的答案上看到所有的 cmets;我不得不直接提到你。
  • @Shepmaster 感谢您指出这一点。我应该在维护一些大表的同时使用static 吗?
  • @AurevoirXavier 这个问题没有统一的答案。您需要使用分析来查看更改是否值得,假设这两种类型都适用于给定情况。
  • 我认为以下链接(甚至“过时”)可能有用 -> doc.rust-lang.org/1.29.2/book/first-edition/…
【解决方案2】:

如果您的变量不打算更改,则没有太大的实际差异。

常量在编译时被内联,这意味着它们被复制到它们使用的每个位置,因此通常更高效,而静态指的是内存中的唯一位置,更像是全局变量。

常量是...常量,而静态变量虽然仍然是全局变量,但可以是可变的。

【讨论】:

    【解决方案3】:

    Rust static vs const

    const:

    • 内存中没有固定地址
    • 它们被内联到每个使用它们的地方,这意味着它们被直接放入使用它们的地方的二进制文件中。
    • 通常运行速度更快,但可执行文件更大,因为它不必查找像static 这样的地址

    static:

    • 在内存中有固定地址
    • 它们的值是从每个使用它们的地方的固定地址加载的。
    • 通常运行时间较慢,因为我们需要执行从固定地址加载数据的额外指令。但是,这可能会导致可执行文件更小(仅在频繁使用时),因为它不必将值的多个副本烘焙到可执行文件中。

    示例:

        static CDF: i32 = 100;
        const ABC: i32 = 50;
    
        fn main() {
            println!("{}", CDF); // compiler will put in a load instruction here for the static address
            println!("{}", ABC); // compiler will put the value 50 here directly
        
            // statics can be mutable
            static mut HI: &str = "hi";
        
            // however using mut static is unsafe
            unsafe {
                HI = "HITHERE";
            }
        
            unsafe {
                println!("{}", HI);
            }
        }
    

    【讨论】:

      【解决方案4】:

      static 的主要目的是允许函数控制一个内部值,该值在调用中被记住,但主应用程序代码无法访问。它类似于类变量,而不是其他语言中的实例变量。 C和PHP等很多语言也有这个概念。

      示例:您想跟踪一个函数被调用了多少次,并有办法重置内部计数器:

      fn counter(reset: bool) -> i32 {
          static mut Count: i32 = 0;
      
          unsafe {
              if reset {
                  Count = 0;
              }    
              Count += 1;
              return Count;
          }
      }
      
      println!("{}",counter(true));
      println!("{}",counter(false));
      println!("{}",counter(false));
      //println!("{}", Count); // Illegal
      

      【讨论】:

      • 虽然这是使用static一种 方式,但我认为我不会将其称为“主要目的”。请注意,static 的 Rust 意义比在 C 函数内部使用 static 更广泛,因为它还用于在函数外部创建全局变量(C 不为此使用 static,而是使用它作为链接修饰符做双重职责)。
      猜你喜欢
      • 2011-01-14
      • 2012-10-22
      • 2011-05-27
      • 2010-09-25
      • 2014-06-25
      • 2021-04-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多