V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
0gre2019
V2EX  ›  Rust

刚开始学 Rust,这是一段创建一个原始数组的代码,有更“官方”的实现吗?

  •  
  •   0gre2019 · 2020-09-20 10:53:22 +08:00 · 4586 次点击
    这是一个创建于 1285 天前的主题,其中的信息可能已经有所发展或是发生改变。
    原 Java 代码:
    int[] arr = new int[10];
    for(int i = 0;i < arr.length;i ++)
    arr[i] = i;

    我捣鼓的 Rust 代码:
    fn main() {
    let mut arr: [i32; 10] = [0; 10];

    for i in 0..arr.len() {
    arr[i] = i as i32;
    }
    }

    有个疑问:
    1. Rust 的原始数组似乎是没有“new”这种特性,即提前开辟一个空间。必须在声明时同时初始化?假如我如下面这样写:

    let mut arr: [i32; 10]; // 仅声明一个数组,未初始化

    那么 cargo check 会在 arr[i] = i as i32;处提示数组未初始化,有比我这个更好的写法吗,即数组初始化方法?

    (另外吐槽下 Rust 真的很严格,循环里的赋值代码必须写 as i32,好像不准隐式转换 usize 到 i32 。。)
    16 条回复    2020-09-23 18:38:40 +08:00
    Jirajine
        1
    Jirajine  
       2020-09-20 11:07:44 +08:00 via Android
    为什么要用原始数组呢,用 vector 啊。
    let v1:Vec<i32> = (1..10).collect();
    Jirajine
        2
    Jirajine  
       2020-09-20 11:15:17 +08:00 via Android
    至于初始化,直接声明的变量是分配在栈上的,编译时就已确定,所以不用额外分配空间。
    所有变量访问时必须初始化是 rust 静态检查的要求,未初始化当然可以赋值,只要访问前赋值了就行。
    你这个数组不能的原因是 arr[i]本身就是一次访问,调用 index 方法,而 rust 不允许访问未初始化的变量。
    0gre2019
        3
    0gre2019  
    OP
       2020-09-20 11:37:25 +08:00
    @Jirajine 因为刚学,想把一个 Java 实现的数据结构用 Rust 重写一下来练习。。
    noe132
        4
    noe132  
       2020-09-20 13:24:14 +08:00
    Java 在你 new 的时候自动帮你把每个值赋值初始值了。
    https://stackoverflow.com/a/3426854/6403587
    CSM
        5
    CSM  
       2020-09-20 13:34:08 +08:00 via Android
    你需要区分开引用类型与值类型。

    > 那么 cargo check 会在 arr[i] = i as i32;处提示数组未初始化

    这里的意思不是说数组的内容没有初始化(事实上,写入没有初始化的内存是允许的,并且是个重要的优化手段),而是指 arr 这个变量名字没有绑定到内存上,
    CSM
        6
    CSM  
       2020-09-20 13:43:54 +08:00
    > let mut arr: [i32; 10]; // 仅声明一个数组,未初始化

    这里只是定义了个名字。

    在 Java 中 `int[] arr` 的意思是定义一个引用,但是因为还没有被赋值,是 null 。
    在 C/C++ 中,`int arr[]` 后就已经在栈为数组预留了空间,此时已经可以写入了(虽然可以读取,但会是一些没用的数据,在 C/C++ 中给出警告,而 Rust 会禁止读取),但是在 Java 中还只是个指针,需要在堆为数组分配了内存(并赋值给这个指针)后才能写入。
    0gre2019
        7
    0gre2019  
    OP
       2020-09-20 15:51:29 +08:00
    @CSM 好像更理解了一些,谢谢
    qefrgty
        8
    qefrgty  
       2020-09-20 16:58:00 +08:00 via Android
    没有。数组必须在声明时直接初始化。

    单就你这段代码,能优化的地方就是第二行代码中的 :[i33;0] 可以删掉。

    写 rust 你就相信编译器并满足它就完事儿了。

    不过不必纠结数组,直接 vec 一把梭(大雾)
    0gre2019
        9
    0gre2019  
    OP
       2020-09-20 19:49:48 +08:00
    @qefrgty : )谢谢
    0gre2019
        10
    0gre2019  
    OP
       2020-09-20 19:51:44 +08:00
    我暂时解决了,大体是用 Box 包装了一下:
    #[derive(Debug)]
    pub struct Array {
    data: Box::<[i32]>,
    size: usize,
    }

    impl Array {
    pub fn new() -> Array {
    Array {
    data: Box::new([0; 100]),
    size: 0,
    }
    }
    }
    ldm0
        11
    ldm0  
       2020-09-20 22:00:33 +08:00
    ```rust
    use std::mem::{self, MaybeUninit};

    fn main() {
    let mut arr = unsafe {
    let data: [MaybeUninit<i32>; 10] = MaybeUninit::uninit().assume_init();
    mem::transmute::<_, [i32; 10]>(data)
    };

    for i in 0..arr.len() {
    arr[i] = i as i32;
    }

    dbg!(arr);
    }
    ```
    参见 <https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html>
    RedL0tus
        12
    RedL0tus  
       2020-09-21 04:55:06 +08:00 via iPhone
    说句题外话(

    原始数组各方面比较接近 C 的数组,用的时候基本上都是使用它的 immutable reference ;应用场景除了 slice 或者像 nom 那样搞 zero-copy 极致性能之外就没多少了(

    就我自己理解的话,一般用 Vec 是最合适的(反正 Vec 可以直接 AsRef 成 &[T]),还有一个 vec! 宏可以用,很方便的(
    0gre2019
        13
    0gre2019  
    OP
       2020-09-21 20:45:14 +08:00
    @ldm0 老哥你这是终极解决方案,我看了 Vec 的源码,基础数据类型就是 MaybeUninit<T>,谢了!
    CSM
        14
    CSM  
       2020-09-22 12:59:05 +08:00 via Android
    @ldm0 是不是 transmute 的太早了……
    ldm0
        15
    ldm0  
       2020-09-23 14:16:29 +08:00
    @CSM 确实是早的,但是题主要的就是这样。
    CSM
        16
    CSM  
       2020-09-23 18:38:40 +08:00 via Android
    @ldm0 不是,你这还没有初始化就转换了,虽说随后就写入了初始化的值,但创建未初始化的内存立马就是 UB 啊,不应该是初始化完成后再转换吗?
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3010 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 14:57 · PVG 22:57 · LAX 07:57 · JFK 10:57
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.