Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Concatenate array slices

Tags:

rust

I have two (very large) arrays foo and bar of the same type. To be able to write some nice code, I would like to obtain a read-only slice, result, of the concatenation of the two arrays. This operation must run in O(1) time and space.

Array access for result must also be in O(1). More generally, if result were the concatenation of k array slices, an arbitrary array access for result should run in O(k).

I do not want to copy any elements of foo nor bar.

This would seem to be easy to implement into the Rust core, but no amount of searching has brought me a solution.

like image 774
Tsukki Avatar asked Jun 02 '16 07:06

Tsukki


People also ask

Does append create a new slice?

append() function creates a new slice, if the length the slice is greater than the length of the array pointed by the slice.

How do I append an array in Golang?

There is no way in Golang to directly append an array to an array. Instead, we can use slices to make it happen. The following characteristics are important to note: Arrays are of a fixed size.


2 Answers

I'm afraid what you are asking is pretty much impossible if you require the result to be an actual slice. A slice is a view into a block of memory. Contiguous memory. If you want a new slice by combining two other slices you have to copy the contents to a new location, so that you get a new contiguous block of memory.

If you are satisfied just concatenating by copying SliceConcatExt provides the methods concat and join on slices, which can be used on slices of custom types as long as they implement Clone:

#[derive(Clone, PartialEq, Debug)]
struct A {
    a: u64,
}

fn main() {
    assert_eq!(["hello", "world"].concat(), "helloworld");
    assert_eq!([[1, 2], [3, 4]].concat(), [1, 2, 3, 4]);
    assert_eq!([[A { a: 1 }, A { a: 2 }], [A { a: 3 }, A { a: 4 }]].concat(),
               [A { a: 1 }, A { a: 2 }, A { a: 3 }, A { a: 4 }]);
}

Note that even though SliceConcatExt is unstable, the methods themselves are stable. So there is no reason not to use them if copying is OK. If you can't copy you can't get a slice. In that case, you need to create a wrapper type, as explained in the answer of ker.

like image 101
Erik Vesteraas Avatar answered Oct 02 '22 22:10

Erik Vesteraas


There isn't a predefined type, but you can easily create your own by implementing the Index trait for a type that holds both your slices:

use std::ops::Index;

struct Slice<'a, T: 'a>(&'a[T], &'a[T]);

impl<'a, T: 'a> Index<usize> for Slice<'a, T> {
    type Output = T;
    fn index(&self, index: usize) -> &T {
        if index < self.0.len() {
            &self.0[index]
        } else {
            &self.1[index - self.0.len()]
        }
    }
}

More generally, if result were the concatenation of k array slices, an arbitrary array access for result should run in O(k).

You can get slice access in O(log(k)), if your slice concatenation is O(k), by creating an array that holds the cumulative lengths of the slices and using a binary search to find the actual slice to index into.

This would require a macro, because we don't have a good enough constant evaluator yet and no value generics.

like image 20
oli_obk Avatar answered Oct 02 '22 20:10

oli_obk