在为结构实现默认特征并使用函数初始化其成员时,这些函数是计算一次还是多次?

When implementing the default trait for a struct, and intializing its members using functions, are those functions evaluated once or several times?

我在箱子里有以下伪代码

pub static struct connection {
  pub Id: i32,
  pub speed: i32,
}

impl Default for connection {
  fn default() -> Self {
      connection {
          Id: open_connection(),
          Speed: get_speed_at_init(),
      }
  }
}

那个 crate 被导入到另一个项目中,并 运行 到几个工作线程中,每个线程在无限循环中使用导入的 crate。

现在我可以使用 self.connection.Id 对其执行网络操作,还是 open_connection() 会被评估多次导致打开太多连接而不是预期的一次?

好几次了。这可以简单地证明:

struct Struct;

impl Default for Struct {
    fn default() -> Self {
        println!("default called");
        Struct
    }
}

fn main() {
    let s1 = Struct::default(); // prints "default called"
    let s2 = Struct::default(); // prints "default called"
}

playground

Default 特征或 default 函数没有什么特别之处。它们像任何其他特性和任何其他功能一样工作。

如果我对你的问题理解正确,你想要一个 Connection 类型的全局可变变量,你想知道每次从不同的 crate/thread 导入变量时是否会重新初始化它。

在这种情况下,您不需要默认特征。 Default 不是特殊特征。它只在标准库中,因为它很常见。要定义全局变量,您必须直接初始化值。您必须先定义类型 Connection:

pub struct Connection {
  pub id: i32,
  pub speed: i32,
}

然后创建全局变量。有许多方法可以创建全局可变变量,如 this answer. Assuming that Connection stores things other than integers you will probably need to wrap the global in an Arc for thread-safety, and a Mutex if you require mutability across threads. You can use lazy_static 中所述,以在运行时初始化变量,这允许您进行必要的方法调用以创建 Connection:

lazy_static! {
    pub static ref conn: Arc<Connection> = Arc::new(Connection {
        id: open_connection(),
        speed: get_speed_at_init(),
    });
}

现在回答你的问题。

不,生成新线程或从板条箱导入变量不会重新初始化静态变量。 static 变量表示程序中的精确内存位置。所有对静态的引用都指向相同的内存位置,无论它们位于相同的模块、板条箱还是线程中。我们可以通过在 open_connection 中生成一个随机 id 并将全局 conn 放在一个单独的模块中来测试它:

pub mod connection {
    lazy_static! {
        pub static ref conn: Arc<Connection> = Arc::new(Connection {
            id: open_connection(),
            speed: get_speed_at_init(),
        });
    }

    fn open_connection() -> i32 {
        let mut rng = rand::thread_rng();
        rng.gen()
    }
}

您可以从多个模块或 crate 访问 conn

mod a {
    use crate::connection::conn;
    pub fn do_stuff() {
        println!("id from a: {}", conn.id);
    }
}

// a different crate
mod b {
    use crate::connection::conn;
    pub fn do_stuff() {
        println!("id from b: {}", conn.id);
    }
}

多线程:

mod c {
    use crate::connection::conn;
    pub fn do_stuff() {
        for i in 0..5 {
            std::thread::spawn(move || {
                println!("id from thread #{}: {}", i, conn.id);
            })
            .join()
            .unwrap();
        }
    }
}

但是 id 将始终引用最初声明全局变量时生成的 id:

fn main() {
    a::do_stuff();
    different_crate::b::do_stuff();
    c::do_stuff();
}

// id from a: 1037769551
// id from b: 1037769551
// id from thread #0: 1037769551
// id from thread #1: 1037769551
// id from thread #2: 1037769551
// id from thread #3: 1037769551
// id from thread #4: 1037769551

Playground link:不幸的是你不能在操场上有多个板条箱,所以模块是我能做的最好的。如果您仍然不确定,可以随时在本地进行测试。