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

2020-09-20 10:53:22 +08:00
 0gre2019
原 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 。。)
4638 次点击
所在节点    Rust
16 条回复
Jirajine
2020-09-20 11:07:44 +08:00
为什么要用原始数组呢,用 vector 啊。
let v1:Vec<i32> = (1..10).collect();
Jirajine
2020-09-20 11:15:17 +08:00
至于初始化,直接声明的变量是分配在栈上的,编译时就已确定,所以不用额外分配空间。
所有变量访问时必须初始化是 rust 静态检查的要求,未初始化当然可以赋值,只要访问前赋值了就行。
你这个数组不能的原因是 arr[i]本身就是一次访问,调用 index 方法,而 rust 不允许访问未初始化的变量。
0gre2019
2020-09-20 11:37:25 +08:00
@Jirajine 因为刚学,想把一个 Java 实现的数据结构用 Rust 重写一下来练习。。
noe132
2020-09-20 13:24:14 +08:00
Java 在你 new 的时候自动帮你把每个值赋值初始值了。
https://stackoverflow.com/a/3426854/6403587
CSM
2020-09-20 13:34:08 +08:00
你需要区分开引用类型与值类型。

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

这里的意思不是说数组的内容没有初始化(事实上,写入没有初始化的内存是允许的,并且是个重要的优化手段),而是指 arr 这个变量名字没有绑定到内存上,
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
2020-09-20 15:51:29 +08:00
@CSM 好像更理解了一些,谢谢
qefrgty
2020-09-20 16:58:00 +08:00
没有。数组必须在声明时直接初始化。

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

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

不过不必纠结数组,直接 vec 一把梭(大雾)
0gre2019
2020-09-20 19:49:48 +08:00
@qefrgty : )谢谢
0gre2019
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
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
2020-09-21 04:55:06 +08:00
说句题外话(

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

就我自己理解的话,一般用 Vec 是最合适的(反正 Vec 可以直接 AsRef 成 &[T]),还有一个 vec! 宏可以用,很方便的(
0gre2019
2020-09-21 20:45:14 +08:00
@ldm0 老哥你这是终极解决方案,我看了 Vec 的源码,基础数据类型就是 MaybeUninit<T>,谢了!
CSM
2020-09-22 12:59:05 +08:00
@ldm0 是不是 transmute 的太早了……
ldm0
2020-09-23 14:16:29 +08:00
@CSM 确实是早的,但是题主要的就是这样。
CSM
2020-09-23 18:38:40 +08:00
@ldm0 不是,你这还没有初始化就转换了,虽说随后就写入了初始化的值,但创建未初始化的内存立马就是 UB 啊,不应该是初始化完成后再转换吗?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/708705

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX