How to create an array of structs containing large arrays?

戏子无情 提交于 2019-12-24 04:57:30

问题


How could I create an array of structs containing big arrays with a fixed size? I want to use arrays and not vectors.

This code is an example but doesn't compile

struct _Tmove {
    data1: usize,
    data2: u64,
    data3: bool,
}

struct _TmoveP {
    data4: Box<[_Tmove]>,
    data5: isize,
}

fn main() {
    let mut gen_list = Box::new([
        _TmoveP {
            data5: 1,
            data4: Box::new([_Tmove { data1: 5, data2: 1, data3: true }; 300]),
        }
        ; 100000]);

    assert!(gen_list[0].data4[0].data1==5);
}
error[E0277]: the trait bound `_Tmove: std::marker::Copy` is not satisfied
--> src/main.rs:16:29
       |
    16 |             data4: Box::new([_Tmove { data1: 5, data2: 1, data3: true }; 300]),
                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the `Copy` trait is required because the repeated element will be copied

error[E0277]: the trait bound `_TmoveP: std::marker::Copy` is not     satisfied
--> src/main.rs:13:33
    |
13  |     let mut gen_list = Box::new([
    |                                 ^
    |
    = note: the `Copy` trait is required because the repeated element will be copied

I am using Rust 1.12.


回答1:


In order to benefit from the initialization syntax: [expr; N], the result of expr need to be Copy (as copies need be made).

#[derive(Copy, Clone)]
struct _Tmove {
    data1: usize,
    data2: u64,
    data3: bool,
}

#[derive(Copy, Clone)]
struct _TmoveP {
    data4: Box<[_Tmove]>,
    data5: isize,
}

However, in this case, _TmoveP cannot be Copy because it contains a Box which is not Copy.

Alright, let's get rid of Box:

#[derive(Copy, Clone)]
struct _TmoveP {
    data4: [_Tmove; 300],
    data5: isize,
}

Sounds great?

But unfortunately, [_Tmove; 300] is not Clone either :( We are unfortunately hitting a limitation of the Rust compiler (it works for size less than 32).

The Copy is easy enough... but first we have to implement Clone manually. The naive way is no fun, but it's easy enough:

impl Clone for _TmoveP {
    fn clone(&self) -> _TmoveP {
        unsafe {
            let mut res = _TmoveP {
                data4: std::mem::uninitialized(),
                data5: self.data5,
            };

            std::ptr::copy_nonoverlapping(
                &self.data4 as *const _Tmove,
                std::mem::transmute(&mut res.data4),
                300
            );

            res
        }
    }
}

Note: for some reason &mut res.data4 as *mut _ would not compile... whatever :x

However, @Francis Gagné reminded me in the comments, there is a weird trick with Copy types:

impl Clone for _TmoveP {
    fn clone(&self) -> _TmoveP { *self }
}

This works, for some reason, and is handy in those situations.

And, finally, this works... oh wait, there's an issue in main!

fn main() {
    let gen_list = Box::new([
        _TmoveP {
            data5: 1,
            data4: [_Tmove { data1: 5, data2: 1, data3: true }; 300],
        }
        ; 100000]);

    assert!(gen_list[0].data4[0].data1==5);
}

Alright, here we go.


What's the deal with arrays only really working for sizes smaller than 32?

Simply put: Rust does not have (yet?) support for non-type generic parameters.

Arrays are special-cased, to an extent, but require implementing traits for each size independently... so the standard library implements its traits for arrays up to size 32, because it seemed a good trade-off.




回答2:


You are receiving this error because you're trying to initialise an array with the default initialisation syntax, but your struct does not implement the Copy trait, so this is not allowed. You can see the reasons here, but, in a nutshell, the default initialisation syntax will create one copy of your struct, then try to copy it 100,000 times. Obviously, this isn't allowed if your struct isn't marked Copy, so an error is raised.

Normally, this could be solved by marking both your structs as Copy, like so:

#[derive(Clone, Copy)]
struct _Tmove {
    data1: usize,
    data2: u64,
    data3: bool,
}

#[derive(Clone, Copy)]
struct _TmoveP {
    data4: Box<[_Tmove]>,
    data5: isize,
}

However, you'll notice that this still doesn't compile, because you've not actually got an array here. You've actually used the type for a slice (take a look at this similar issue). Slices don't implement Copy, so your code cannot compile because the _TmoveP struct can only derive Copy if all its fields are Copy.

It's unclear whether the array will always have a fixed size. If it will, then you need to use the type [T; N], where T is your type and N is the number of elements (e.g. [i32; 300]). If not, you need a Vec<T>.

If you use an array, you hit yet another problem. Arrays implement Copy (up to 32), but not Clone, and we need to implement Clone for _TmoveP before we can implement Copy. So, let's do it ourselves:

impl Clone for _TmoveP {
    fn clone(&self) -> _TmoveP {
        _TmoveP {
            data4: self.data4,
            data5: self.data5
        }
    }
}

You can then remove the #[derive(Clone)] from _TmoveP (just leaving Copy), and we finally reach a working solution! Here is the playground link for my solution.



来源:https://stackoverflow.com/questions/39924909/how-to-create-an-array-of-structs-containing-large-arrays

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!