< 返回版块

hzqd 发表于 2021-12-15 12:48

https://play.rust-lang.org/?version=stable&mode=release&edition=2021&gist=9a463c5f8b8ee51ded6cc9602ff0566f

use rand::Rng; // 0.8.4

#[derive(Debug)]
struct A {
    a: i32 // a 的类型是 i32,说明是立即求值的,非惰性(否则应为 Box<FnOnce() -> i32>)
}

impl A {
    fn new() -> A {
        A { a: rand::thread_rng().gen::<i32>() } // 这里应当“编译期求值”、“立即求值”,还是“惰性求值”?
    }
}

fn main() {
    dbg!(A::new()); // 如果是“编译期求值”或“立即求值”,那这个值应该是固定的
    dbg!(A::new()); // 但结果不一致,难道字段`a`是函数?
}

Rust 结构体的字段是如何被执行的?

评论区

写评论
xiaopengli89 2021-12-16 14:22

和 Copy 没有关系,而是和有没有实现 Drop 有关:

对于没有实现 Drop 的 const 常量引用会表现为 static,而对于实现了 Drop 的 const 常量引用,实际上会在使用的地方独立创建一份拷贝:

struct A<T> {
    inner: Option<T>,
}

const fn new<T>() -> A<T> {
    A { inner: None, }
}

const B: A<Foo> = new();

struct Foo;

// 注释下面的 Drop 实现,&B 会表现为一个 static 地址
impl Drop for Foo {
    fn drop(&mut self) {}
}

fn main() {
    println!("{:p}", &B);
    println!("{:p}", &B);
}

--
👇
Freddie Mercury: 是的,看出来了。

对于实现了Copytrait的类型,const都是指向相同的内存地址,对于在堆上的,编译时等于执行了Clone

--
👇
Bai-Jinlin: const还是有些奇怪的规则的

struct A<T> {
    inner: Option<T>,
}
const fn new<T>() -> A<T> {
    A { inner: None }
}
const B: A<String> = new();
fn main() {
    println!("{:p}", &B);
    println!("{:p}", &B);
}

比如这个你就能看到不同的地址,把String改成i32地址就一样了,所以const其实不是你下面那么用的。

--
👇
Freddie Mercury: 编译时赋值,貌似只能使用const fn,但目前这个限制颇多,而且官方文档里明确说明产生随机值这种情况是绝对不可能的。

Const functions have various restrictions to make sure that they can be evaluated at compile-time. It is, for example, not possible to write a random number generator as a const function.

我看到一个nightly的module std::lazy,里面可以定义一个Lazy的结构体,通过解引用访问,只执行一次,可能就是楼主想要的功能。

但自己试了几把,感觉多了点其它的疑问。。。

#![feature(once_cell)]
use rand::Rng; // 0.8.4
use std::lazy::Lazy;

#[derive(Debug, Clone)]
struct A {
    a: i32, 
}

const CONST_LAZY: Lazy<i32> = Lazy::new(|| rand::thread_rng().gen::<i32>());

impl A {
    fn lazy_new() -> Lazy<Self> {
        Lazy::new(|| A {
            a: rand::thread_rng().gen::<i32>(),
        })
    }

    fn new() -> A {
        A::lazy_new().clone()
    }

    fn new_para(lazy: i32) -> A {
        A { a: lazy }
    }
}

fn main() {
    // 可以理解,因为lazy_new返回了两个lazy对象
    dbg!(A::new()); 
    dbg!(A::new());

    // 常量出现了两个值????
    // 而且两次执行值会发生变化,显然还是运行时赋值
    // 推测是const只是指向了lazy闭包
    println!("{}", *CONST_LAZY);
    println!("{}", *CONST_LAZY);

    // 实现的很丑陋,但是值是一样了,问题是这个还是运行时赋值
    let para: Lazy<i32> = Lazy::new(|| rand::thread_rng().gen::<i32>());
    dbg!(A::new_para(*para));
    dbg!(A::new_para(*para));
}

Mercury 2021-12-16 09:58

是的,看出来了。

对于实现了Copytrait的类型,const都是指向相同的内存地址,对于在堆上的,编译时等于执行了Clone

--
👇
Bai-Jinlin: const还是有些奇怪的规则的

struct A<T> {
    inner: Option<T>,
}
const fn new<T>() -> A<T> {
    A { inner: None }
}
const B: A<String> = new();
fn main() {
    println!("{:p}", &B);
    println!("{:p}", &B);
}

比如这个你就能看到不同的地址,把String改成i32地址就一样了,所以const其实不是你下面那么用的。

--
👇
Freddie Mercury: 编译时赋值,貌似只能使用const fn,但目前这个限制颇多,而且官方文档里明确说明产生随机值这种情况是绝对不可能的。

Const functions have various restrictions to make sure that they can be evaluated at compile-time. It is, for example, not possible to write a random number generator as a const function.

我看到一个nightly的module std::lazy,里面可以定义一个Lazy的结构体,通过解引用访问,只执行一次,可能就是楼主想要的功能。

但自己试了几把,感觉多了点其它的疑问。。。

#![feature(once_cell)]
use rand::Rng; // 0.8.4
use std::lazy::Lazy;

#[derive(Debug, Clone)]
struct A {
    a: i32, 
}

const CONST_LAZY: Lazy<i32> = Lazy::new(|| rand::thread_rng().gen::<i32>());

impl A {
    fn lazy_new() -> Lazy<Self> {
        Lazy::new(|| A {
            a: rand::thread_rng().gen::<i32>(),
        })
    }

    fn new() -> A {
        A::lazy_new().clone()
    }

    fn new_para(lazy: i32) -> A {
        A { a: lazy }
    }
}

fn main() {
    // 可以理解,因为lazy_new返回了两个lazy对象
    dbg!(A::new()); 
    dbg!(A::new());

    // 常量出现了两个值????
    // 而且两次执行值会发生变化,显然还是运行时赋值
    // 推测是const只是指向了lazy闭包
    println!("{}", *CONST_LAZY);
    println!("{}", *CONST_LAZY);

    // 实现的很丑陋,但是值是一样了,问题是这个还是运行时赋值
    let para: Lazy<i32> = Lazy::new(|| rand::thread_rng().gen::<i32>());
    dbg!(A::new_para(*para));
    dbg!(A::new_para(*para));
}

Bai-Jinlin 2021-12-15 17:17

const还是有些奇怪的规则的

struct A<T> {
    inner: Option<T>,
}
const fn new<T>() -> A<T> {
    A { inner: None }
}
const B: A<String> = new();
fn main() {
    println!("{:p}", &B);
    println!("{:p}", &B);
}

比如这个你就能看到不同的地址,把String改成i32地址就一样了,所以const其实不是你下面那么用的。

--
👇
Freddie Mercury: 编译时赋值,貌似只能使用const fn,但目前这个限制颇多,而且官方文档里明确说明产生随机值这种情况是绝对不可能的。

Const functions have various restrictions to make sure that they can be evaluated at compile-time. It is, for example, not possible to write a random number generator as a const function.

我看到一个nightly的module std::lazy,里面可以定义一个Lazy的结构体,通过解引用访问,只执行一次,可能就是楼主想要的功能。

但自己试了几把,感觉多了点其它的疑问。。。

#![feature(once_cell)]
use rand::Rng; // 0.8.4
use std::lazy::Lazy;

#[derive(Debug, Clone)]
struct A {
    a: i32, 
}

const CONST_LAZY: Lazy<i32> = Lazy::new(|| rand::thread_rng().gen::<i32>());

impl A {
    fn lazy_new() -> Lazy<Self> {
        Lazy::new(|| A {
            a: rand::thread_rng().gen::<i32>(),
        })
    }

    fn new() -> A {
        A::lazy_new().clone()
    }

    fn new_para(lazy: i32) -> A {
        A { a: lazy }
    }
}

fn main() {
    // 可以理解,因为lazy_new返回了两个lazy对象
    dbg!(A::new()); 
    dbg!(A::new());

    // 常量出现了两个值????
    // 而且两次执行值会发生变化,显然还是运行时赋值
    // 推测是const只是指向了lazy闭包
    println!("{}", *CONST_LAZY);
    println!("{}", *CONST_LAZY);

    // 实现的很丑陋,但是值是一样了,问题是这个还是运行时赋值
    let para: Lazy<i32> = Lazy::new(|| rand::thread_rng().gen::<i32>());
    dbg!(A::new_para(*para));
    dbg!(A::new_para(*para));
}

Mercury 2021-12-15 16:06

编译时赋值,貌似只能使用const fn,但目前这个限制颇多,而且官方文档里明确说明产生随机值这种情况是绝对不可能的。

Const functions have various restrictions to make sure that they can be evaluated at compile-time. It is, for example, not possible to write a random number generator as a const function.

我看到一个nightly的module std::lazy,里面可以定义一个Lazy的结构体,通过解引用访问,只执行一次,可能就是楼主想要的功能。

但自己试了几把,感觉多了点其它的疑问。。。

#![feature(once_cell)]
use rand::Rng; // 0.8.4
use std::lazy::Lazy;

#[derive(Debug, Clone)]
struct A {
    a: i32, 
}

const CONST_LAZY: Lazy<i32> = Lazy::new(|| rand::thread_rng().gen::<i32>());

impl A {
    fn lazy_new() -> Lazy<Self> {
        Lazy::new(|| A {
            a: rand::thread_rng().gen::<i32>(),
        })
    }

    fn new() -> A {
        A::lazy_new().clone()
    }

    fn new_para(lazy: i32) -> A {
        A { a: lazy }
    }
}

fn main() {
    // 可以理解,因为lazy_new返回了两个lazy对象
    dbg!(A::new()); 
    dbg!(A::new());

    // 常量出现了两个值????
    // 而且两次执行值会发生变化,显然还是运行时赋值
    // 推测是const只是指向了lazy闭包
    println!("{}", *CONST_LAZY);
    println!("{}", *CONST_LAZY);

    // 实现的很丑陋,但是值是一样了,问题是这个还是运行时赋值
    let para: Lazy<i32> = Lazy::new(|| rand::thread_rng().gen::<i32>());
    dbg!(A::new_para(*para));
    dbg!(A::new_para(*para));
}

zhylmzr 2021-12-15 14:34

头一回听到惰性这个词,在rust中除了literal,只有static声明的变量或函数是在编译期计算的,其他的调用和赋值都是在程序运行执行到才会计算。

退一步说,哪怕gen声明为static,你调用了两次new,就调用了2次gen,2次gen调用返回的值一定是一样的吗?

xiaopengli89 2021-12-15 13:46

new 是一个函数,返回一个 A 的值,你返回了 2 个 A, 我看你想表达的是这种:

let a_instance = A::new();
dbg!(a_instance.a);
dbg!(a_instance.a);
xiaopengli89 2021-12-15 13:44

a 是 i32 类型的值; rand::thread_rng().gen::() 是一个表达式,其结果是 i32 类型的值; 每次调用 rand::thread_rng().gen::() 都会返回一个随机的 i32 类型的值,你调用了2次,结果不同很正常; const function 才是编译期求值;

munpf 2021-12-15 13:07

每次执行new的时候,都会执行一次gen,肯定不一样了

1 共 8 条评论, 1 页