1

I want to reshape the standard Vec. Although this can be achieved using a for loop, I'm wondering if there is a better way. I'd like to reshape this:

let before = [1, 2, 3, 4, 5, 6, 7, 8, 9, /* ... */];

Into this:

let after = [[1,4,7], [2,5,8], [3,6,9], /* ... */];

It should be flexible such that I can make a function with a size argument, example:

let size_2 = [[1,3], [2,4], [3,5], /* ... */]; // size = 2
pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
Selva
  • 951
  • 7
  • 23
  • Please try to use type identifiers to help explain what you want. For instance, what is the type of `[[1, 4, 7], ...`? This is ambiguous. – chub500 May 07 '20 at 20:53

2 Answers2

0

chunks method converts a slice (which can be obtained from vectors, arrays and a few other objects) into non-intersecting chunks. The function results with an iterable object, which has to be converted into something meaningful. In this example I make a vector of chunks via collect function on iterator.

fn main() {
    let y = [1,2,3,4,5,6,7,8,9];
    let split_y: Vec<_> = y.chunks(4).collect(); 
    print!("{:?}", split_y); // [[1, 2, 3, 4], [5, 6, 7, 8], [9]]
}

Look also for chunks_exact there if you want to process the [9] tail into other form.

Alexey S. Larionov
  • 6,555
  • 1
  • 18
  • 37
  • Fun API - I didn't realize that existed. Should be noted to the OP though that this will create a _new_ vector. If OP is coming from tensorflow background or something that has a reshape operation, this is a very different thing. – chub500 May 07 '20 at 20:02
  • 1
    @chub500 then just don't create a new vector (don't call `collect`), and just use chunks as lazy iterator – Alexey S. Larionov May 07 '20 at 20:09
-1

Your requirements specify this must be a 'reshape' on a standard Vec. I take this to mean you wish to do a reinterpret_cast (to steal terminology from c++) on an existing vector in place.

The only way I know of to do this is unsafe. Using transmute you can reinterpret your Vec<usize> into say Vec<[usize; 4]>.

If you're willing to give up the 'standard' Vec, there are some decent tensor crates available.

chub500
  • 712
  • 6
  • 18
  • Transmuting a `Vec` directly into a `Vec` [is not guaranteed to work](https://doc.rust-lang.org/nomicon/transmutes.html) for T != U. This is because `Vec` and `Vec` might be laid out in memory differently even if T and U are laid out the same. You need to separate the `Vec` into its pieces, as in [this example](https://doc.rust-lang.org/std/vec/struct.Vec.html#examples-3), and cast/transmute the underlying pointer before calling `from_raw_parts` to turn it into a `Vec` again, or you risk UB. – Joshua Little Jan 27 '22 at 20:39
  • Perhaps I was missing the point of this question but I interpreted it as a reference to [1] "The tf.reshape does not change the order of or the total number of elements in the tensor, and so it can reuse the underlying data buffer. This makes it a fast operation independent of how big of a tensor it is operating on." [1] https://www.tensorflow.org/api_docs/python/tf/reshape – chub500 Jan 27 '22 at 21:54
  • I'm just commenting on your statement "Using transmute you can reinterpret your `Vec` into say `Vec<[usize; 4]>`." Following that advice invokes undefined behavior. – Joshua Little Jan 27 '22 at 23:03
  • Yes, your statement is correct but any time you reinterpret a block of memory of one type as another type you are doing something 'unsafe', which is why transmute is an unsafe API. Of course, you can make your own internal wrapper around the transmute to hide the 'unsafe' from the caller but somewhere a transmute will be called if you want to avoid looping through the array. – chub500 Jan 31 '22 at 16:14
  • "Undefined behavior" isn't the same as "unsafe." Undefined behavior means your code is broken, even if it's in an unsafe block. See https://manishearth.github.io/blog/2017/12/24/undefined-vs-unsafe-in-rust/ for one explanation. – Joshua Little Feb 01 '22 at 17:14
  • Again, agreed. All undefined code is unsafe, but not all unsafe code is undefined. All transmutes are unsafe but not all transmutes are undefined. Sometimes (like in the case above) they're necessary to get the right behavior, in this case no loops for something you know don't need loops. – chub500 Feb 01 '22 at 17:33
  • The transmute you're advising is undefined behaviour: https://doc.rust-lang.org/nomicon/transmutes.html. "So how do you know if the layouts are the same? For `repr(C)` types and `repr(transparent)` types, layout is precisely defined. But for your run-of-the-mill `repr(Rust)` it is not. Even different instances of the same generic type can have wildly different layout. `Vec` and `Vec` might have their fields in the same order, or they might not." – Joshua Little Feb 01 '22 at 19:55
  • Yep, agreed. Use with caution. If you have a better solution feel free to post your answer. I'd be happy to see real alternatives that don't involve walking the array. – chub500 Feb 01 '22 at 20:05
  • Here's an example: https://stackoverflow.com/a/55081958/155423. You do have to make sure the pointer is properly aligned for the new type, though. This means that something like `Vec` to `Vec` might not work, since the target type has stricter alignment requirements than the source (on most platforms), in which case you *have* to copy the data to properly align it if you want a `Vec` at the end. – Joshua Little Feb 01 '22 at 20:36