0

I want to make a large square array, but the dimension limit is 1023. Therefore, I want to make a Vec of 1D-arrays, as suggested in a comment. I tried this as a start:

fn main() { 
    const VMAX: usize = 1000; 
    const ALEN: usize = 32; 
    let mut arr2: Vec<[f64; ALEN]> = vec![[0.0; ALEN]; VMAX];}

which is fine for any value of VMAX, but ALEN larger than 32 gives:

error[E0277]: the trait bound `[f64; 33]: std::clone::Clone` is not satisfied
 --> <std macros>:2:1
  |
2 | $ crate :: vec :: from_elem ( $ elem , $ n ) ) ; ( $ ( $ x : expr ) , * ) => (
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
tes3.rs:4:35: 4:58 note: in this expansion of vec! (defined in <std macros>)
  |
  = help: the following implementations were found:
  = help:   <[T; 0] as std::clone::Clone>
  = help:   <[T; 1] as std::clone::Clone>
  = help:   <[T; 2] as std::clone::Clone>
  = help:   <[T; 3] as std::clone::Clone>
  = help: and 29 others
  = note: required by `std::vec::from_elem`

Something is going wrong and I have problems finding info and interpreting the error message. How can I make my giant square matrix? A different approach would be fine too, performance is important. Of course, I would do something with the array, like looping over the indices and putting some values in it, for example.

Community
  • 1
  • 1
Jonatan Öström
  • 2,428
  • 1
  • 16
  • 27
  • 1
    *the dimension limit is 1023* — no, it isn't. That's like saying that the "memory limit" is 4GB because that's all your computer has. Yes, stack space is limited, but it can usually be changed within some bounds. Additionally, if the item in the array takes 1MB, you would be unlikely to place 1023 of them in an array, and if the item takes a single byte, you can probably place more than 1023. – Shepmaster Oct 26 '16 at 15:27
  • Duplicate of http://stackoverflow.com/q/25151890/155423 or http://stackoverflow.com/q/13102786/155423 or http://stackoverflow.com/q/28145732/155423 or http://stackoverflow.com/q/39240360/155423. **Please** respect the time of answerers by doing a search before asking questions. The specific error you are experiencing is probably explained by http://stackoverflow.com/q/30415354/155423. – Shepmaster Oct 26 '16 at 15:36
  • 1
    In addition, there are [a few crates](https://crates.io/) that deal with numeric and scientific computing. The authors of those crates have already put a good amount of thought into these decisions. One that I'm aware of is [ndarray](https://github.com/bluss/rust-ndarray). – Shepmaster Oct 26 '16 at 15:38
  • 1
    I described my problem and gave my code. Before that I searched hard, but not for "cloning" or "crate" to create a matrix. I have not shown disrespect toward anyone by not using their library that I didn't know existed in a language that I tried for the first time two days ago. In retrospect, the limit of 1023 was ignorantly stated as a fact, but I just experienced it and got it confirmed by other users. So, I am not particularly sorry, but thanks for the clarifications and tips! – Jonatan Öström Oct 26 '16 at 17:06
  • I apologize, I didn't intend to suggest that you were showing disrespect to crate authors. However, A [Google search for "rust 2d"](https://www.google.com/?q=site:stackoverflow.com+rust+2D) provides most of the questions I just linked. That's the primary thing I would have expected someone to do before asking a question. Everything else I said is just pointing you to further information. – Shepmaster Oct 26 '16 at 17:19

1 Answers1

2

You are hitting the issue that Rust does not support non-type generic parameters yet, and therefore traits on [T; N] are only implemented for values of N from 0 to 32 (inclusive). Yes, it's annoying.

A simple work-around is to use a loop:

let v: Vec<_> = (0..MAX).map(|_| [0.; MAX]).collect();

which is close to what the vec! macro does under the hood1.

Otherwise it would be possible to wrap your array in a custom type and implement Clone for it (and Index). It's also a possibility.


1 vec![T; N] is a bit smarter and will ensure memory is only allocated once rather than having multiple allocations. However, according to @bluss, who has been contributed an impressive number of quality performance improvements to the library, collect should soon become smart enough to do a single allocation for most common iterators, too.

bluss
  • 12,472
  • 1
  • 49
  • 48
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • *what the `vec!` macro does under the hood.* — Not quite, it's actually [a bit more efficient than that](https://github.com/rust-lang/rust/blob/91f34c0c70746f5c938d25d02a8a66b41240b2f0/src/libcollections/macros.rs#L45-L53). – Shepmaster Oct 26 '16 at 15:45
  • @Shepmaster: How? It invokes `from_elem` which itself invokes [`extend_with_element`](https://doc.rust-lang.org/src/collections/up/src/libcollections/vec.rs.html#1045) which uses a loop. Is there a specialization somewhere? – Matthieu M. Oct 26 '16 at 15:53
  • Sorry, I was playing a bit fast-and-loose with my wording. The `vec![X; N]` form you were saying is probably basically a loop. The `vec![a, b, c]` form is not a loop though. – Shepmaster Oct 26 '16 at 15:54
  • 1
    How, extend_with_element_uses a loop where there is no reallocation condition inside the body of the loop, so it can be much more efficient. There's a PR in the pipeline to fix up Vec's collect to be equivalent if the iterator is trusted to tell its exact length. – bluss Oct 26 '16 at 16:01
  • 1
    @bluss: I blame Shepmaster! :D (I used `reserve` in the initial version, but it was ugly if faster... glad to know `collect` will be improved so that we can have both elegant and efficient) – Matthieu M. Oct 26 '16 at 16:53
  • @bluss what's your feeling; how would `Vec::new(); vec.reserve(); for _ in range { vec.push(X) }` compare to `range.map(X).collect`? – Shepmaster Oct 26 '16 at 17:12
  • @bluss: Thanks :) – Matthieu M. Oct 27 '16 at 06:23
  • And if I got everything right now, this is how it works: `<_>` means no type is specified. `(0..MAX)` is a tuple of the integers 0 to MAX. `|_| [0.;MAX]` is a *closure* (a function) taking any item `_` returning the array `[0.;MAX]` of size MAX filled with `0.`s. `map()` calls the closure for every item in the tuple producing arrays that `collect()` puts together into a Vec. From `0.` the type `f64` is inferred for the entries in the Vec. – Jonatan Öström Oct 30 '16 at 11:07
  • @JonatanÖström: Close! A tuple is declared by separating its elements with a comma as in `(0, MAX)`. `0..MAX` (no parenthesis needed) is a [`Range`](https://doc.rust-lang.org/std/ops/struct.Range.html), which implements the `Iterator` trait (from where the `map` method comes from) and will iterate over the half-open `[0, MAX)` range. Other ranges include `..` (`RangeFull`), `0..` (`RangeFrom`) and `..MAX` (`RangeTo`). There is also a proposal for `0...MAX` (3 dots) being an inclusive range. – Matthieu M. Oct 30 '16 at 11:20
  • Ah, thanks that makes a lot of sense! Those parenthesis got me thinking of tuples. And I think that triple dot is a really good idea. Then with `1...MAX` you will have a clearly stated one-based indexing, which is nice if you are used to that. – Jonatan Öström Oct 30 '16 at 14:10
  • @JonatanÖström: I am on the fence regarding the triple dot. If only because the difference between `..` and `...` is very small syntactically and therefore this seems like a small typo could easily lead to off-by-one error. I'd rather have something more visually distinctive so that it's noticeable at a glance whether the range is half-open or closed. – Matthieu M. Oct 30 '16 at 15:23
  • Well I get that. I guess features that are syntactically allowed to be interchanged, should differ in syntax by more than a typical typo, to get a compile time error instead of unexpected run time behavior. At the same time one wants associated syntax for associated features, maybe something like `1..:MAX` would be much less typo prone. – Jonatan Öström Oct 30 '16 at 17:22