Assign reference to array of different sizes - rust

I have a function which selects a different array based on whether a boolean is set to true or false, similar to the following:
const V1: [u8; 2] = [1,2];
const V2: [u8; 4] = [1,2,3,4];
fn test(b: bool) {
let v = if b { &V1 } else { &V2 };
}
fn main() {
test(false);
}
However I get the following error:
error[E0308]: `if` and `else` have incompatible types
--> src/main.rs:5:33
|
5 | let v = if b { &V1 } else { &V2 };
| --- ^^^ expected an array with a fixed size of 2 elements, found one with 4 elements
| |
| expected because of this
|
= note: expected type `&[u8; 2]`
found reference `&[u8; 4]`
I tried storing the constants as vectors, but to_vec cannot be used for constants.
An alternative would be to copy the array into a vector inside test, but I'd rather not have to make copies every time.
Is there a way to do this without copying the array every whenever the function is called?

The answer is to use slices, but unfortunately the rust type inference isn't clever enough to realize that. If you annotate the type of v explicitly as an &[u8] then everything should compile.
const V1: [u8; 2] = [1,2];
const V2: [u8; 4] = [1,2,3,4];
fn test(b: bool) {
let v: &[u8] = if b { &V1 } else { &V2 };
}
fn main() {
test(false);
}

Rust must know the type and lifetime of all variables at compile time; in this case, you are using the [u8] type, which is a slice type for u8 elements. Slices are stored as a reference to the first element as well as the number of elements. For example, your V1 slice stores u8 elements and there are 2 of them and V2 stores u8 elements and there are 4 of them. But note that these two are not the same type because of the difference in the number of elements.
So, if you'd like to return a borrowed value of one of the two slices (V1 or V2) you are able to do so as long as the compiler has two pieces of information; the type and their lifetime. We know that the compiler can figure out the type of both V1 and V2 since it is explicitly declared and they both live in static memory (data is part of the program source), so all we have to do is say that we are returning a reference to a slice (borrow) of u8s and they will be around for as long as the program is running (static lifetime). And even though V1 and V2 are not the same type, they look the same when you borrow them since all we are saying is that the return value references a bunch of u8 elements and we leave it up to the compiler to make sure it knows the number of elements for each at compile time. Check out the working example below.
const V1: [u8; 2] = [1,2];
const V2: [u8; 4] = [1,2,3,4];
fn test(b: bool) -> &'static [u8] {
let v: &[u8] = if b { &V1 } else { &V2 };
v
}
fn main() {
println!("{:?}", test(false));
}
As a final note, when attempting to solve these problems, don't be afraid to make mistakes; the compiler is actually quite friendly and very helpful when trying to figure out what to do next as shown in the error message below.
|
6 | fn test(b: bool) -> &[u8] {
| ^ help: consider giving it an explicit bounded or 'static lifetime: `&'static`
|
= help: this function's return type contains a borrowed value with an elided lifetime, but the lifetime cannot be derived from the arguments

Related

How to use map function to collect an array of string? [duplicate]

I want to call .map() on an array of enums:
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let foos = bar.iter().map(|x| Foo::Value(*x)).collect::<[Foo; 3]>();
}
but the compiler complains:
error[E0277]: the trait bound `[Foo; 3]: std::iter::FromIterator<Foo>` is not satisfied
--> src/main.rs:8:51
|
8 | let foos = bar.iter().map(|x| Foo::Value(*x)).collect::<[Foo; 3]>();
| ^^^^^^^ a collection of type `[Foo; 3]` cannot be built from an iterator over elements of type `Foo`
|
= help: the trait `std::iter::FromIterator<Foo>` is not implemented for `[Foo; 3]`
How do I do this?
The issue is actually in collect, not in map.
In order to be able to collect the results of an iteration into a container, this container should implement FromIterator.
[T; n] does not implement FromIterator because it cannot do so generally: to produce a [T; n] you need to provide n elements exactly, however when using FromIterator you make no guarantee about the number of elements that will be fed into your type.
There is also the difficulty that you would not know, without supplementary data, which index of the array you should be feeding now (and whether it's empty or full), etc... this could be addressed by using enumerate after map (essentially feeding the index), but then you would still have the issue of deciding what to do if not enough or too many elements are supplied.
Therefore, not only at the moment one cannot implement FromIterator on a fixed-size array; but even in the future it seems like a long shot.
So, now what to do? There are several possibilities:
inline the transformation at call site: [Value(1), Value(2), Value(3)], possibly with the help of a macro
collect into a different (growable) container, such as Vec<Foo>
...
Update
This can work:
let array: [T; N] = something_iterable.[into_]iter()
.collect::<Vec<T>>()
.try_into()
.unwrap()
In newer version of rust, try_into is included in prelude, so it is not necessary to use std::convert::TryInto. Further, starting from 1.48.0, array support directly convert from Vec type, signature from stdlib source:
fn try_from(mut vec: Vec<T, A>) -> Result<[T; N], Vec<T, A>> {
...
}
Original Answer
as of rustc 1.42.0, if your element impl Copy trait, for simplicity, this just works:
use std::convert::TryInto;
...
let array: [T; N] = something_iterable.[into_]iter()
.collect::<Vec<T>>()
.as_slice()
.try_into()
.unwrap()
collect as_slice try_into + unwrap()
Iterator<T> ------> Vec<T> -------> &[T] ------------------> [T]
But I would just call it a workaround.
You need to include std::convert::TryInto because the try_into method is defined in the TryInto trait.
Below is the signature checked when you call try_into as above, taken from the source. As you can see, that requires your type T implement Copy trait, so theoritically, it will copy all your elements once.
#[stable(feature = "try_from", since = "1.34.0")]
impl<T, const N: usize> TryFrom<&[T]> for [T; N]
where
T: Copy,
[T; N]: LengthAtMost32,
{
type Error = TryFromSliceError;
fn try_from(slice: &[T]) -> Result<[T; N], TryFromSliceError> {
<&Self>::try_from(slice).map(|r| *r)
}
}
While you cannot directly collect into an array for the reasons stated by the other answers, that doesn't mean that you can't collect into a data structure backed by an array, like an ArrayVec:
use arrayvec::ArrayVec; // 0.7.0
use std::array;
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let foos: ArrayVec<_, 3> = array::IntoIter::new(bar).map(Foo::Value).collect();
let the_array = foos
.into_inner()
.unwrap_or_else(|_| panic!("Array was not completely filled"));
// use `.expect` instead if your type implements `Debug`
}
Pulling the array out of the ArrayVec returns a Result to deal with the case where there weren't enough items to fill it; the case that was discussed in the other answers.
For your specific problem, Rust 1.55.0 allows you to directly map an array:
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let foos = bar.map(Foo::Value);
}
In this case you can use Vec<Foo>:
#[derive(Debug)]
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let foos = bar.iter().map(|&x| Foo::Value(x)).collect::<Vec<Foo>>();
println!("{:?}", foos);
}
.collect() builds data structures that can have arbitrary length, because the iterator's item number is not limited in general. (Shepmaster's answer already provides plenty details there).
One possibility to get data into an array from a mapped chain without allocating a Vec or similar is to bring mutable references to the array into the chain. In your example, that'd look like this:
#[derive(Debug, Clone, Copy)]
enum Foo {
Value(i32),
Nothing,
}
fn main() {
let bar = [1, 2, 3];
let mut foos = [Foo::Nothing; 3];
bar.iter().map(|x| Foo::Value(*x))
.zip(foos.iter_mut()).for_each(|(b, df)| *df = b);
}
The .zip() makes the iteration run over both bar and foos in lockstep -- if foos were under-allocated, the higher bars would not be mapped at all, and if it were over-allocated, it'd keep its original initialization values. (Thus also the Clone and Copy, they are needed for the [Nothing; 3] initialization).
You can actually define a Iterator trait extension to do this!
use std::convert::AsMut;
use std::default::Default;
trait CastExt<T, U: Default + AsMut<[T]>>: Sized + Iterator<Item = T> {
fn cast(mut self) -> U {
let mut out: U = U::default();
let arr: &mut [T] = out.as_mut();
for i in 0..arr.len() {
match self.next() {
None => panic!("Array was not filled"),
Some(v) => arr[i] = v,
}
}
assert!(self.next().is_none(), "Array was overfilled");
out
}
}
impl<T, U: Iterator<Item = T>, V: Default + AsMut<[T]>> CastExt<T, V> for U { }
fn main () {
let a: [i32; 8] = (0..8).map(|i| i * 2).cast();
println!("{:?}", a); // -> [0, 2, 4, 6, 8, 10, 12, 14]
}
Here's a playground link.
This isn't possible because arrays do not implement any traits. You can only collect into types which implement the FromIterator trait (see the list at the bottom of its docs).
This is a language limitation, since it's currently impossible to be generic over the length of an array and the length is part of its type. But, even if it were possible, it's very unlikely that FromIterator would be implemented on arrays because it'd have to panic if the number of items yielded wasn't exactly the length of the array.
You may combine arrays map method with Iterator::next.
Example:
fn iter_to_array<Element, const N: usize>(mut iter: impl Iterator<Item = Element>) -> [Element; N] {
// Here I use `()` to make array zero-sized -> no real use in runtime.
// `map` creates new array, which we fill by values of iterator.
let res = [(); N].map(|_| iter.next().unwrap());
// Ensure that iterator finished
assert!(matches!(iter.next(), None));
res
}
I ran into this problem myself — here's a workaround.
You can't use FromIterator, but you can iterate over the contents of a fixed-size object, or, if things are more complicated, indices that slice anything that can be accessed. Either way, mutation is viable.
For example, the problem I had was with an array of type [[usize; 2]; 4]:
fn main() {
// Some input that could come from another function and thus not be mutable
let pairs: [[usize; 2]; 4] = [[0, 0], [0, 1], [1, 1], [1, 0]];
// Copy mutable
let mut foo_pairs = pairs.clone();
for pair in foo_pairs.iter_mut() {
// Do some operation or other on the fixed-size contents of each
pair[0] += 1;
pair[1] -= 1;
}
// Go forth and foo the foo_pairs
}
If this is happening inside a small function, it's okay in my book. Either way, you were going to end up with a transformed value of identical type as the same one, so copying the whole thing first and then mutating is about the same amount of effort as referencing a value in a closure and returning some function of it.
Note that this only works if you plan to compute something that is going to be the same type, up to and including size/length. But that's implied by your use of Rust arrays. (Specifically, you could Value() your Foos or Nothing them as you like, and still be within type parameters for your array.)

Why does size for value types matter when returning a value vs a reference? [duplicate]

I'm trying to manipulate a string derived from a function parameter and then return the result of that manipulation:
fn main() {
let a: [u8; 3] = [0, 1, 2];
for i in a.iter() {
println!("{}", choose("abc", *i));
}
}
fn choose(s: &str, pad: u8) -> String {
let c = match pad {
0 => ["000000000000000", s].join("")[s.len()..],
1 => [s, "000000000000000"].join("")[..16],
_ => ["00", s, "0000000000000"].join("")[..16],
};
c.to_string()
}
On building, I get this error:
error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied
--> src\main.rs:9:9
|
9 | let c = match pad {
| ^ `str` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `str`
= note: all local variables must have a statically known size
What's wrong here, and what's the simplest way to fix it?
TL;DR Don't use str, use &str. The reference is important.
The issue can be simplified to this:
fn main() {
let demo = "demo"[..];
}
You are attempting to slice a &str (but the same would happen for a String, &[T], Vec<T>, etc.), but have not taken a reference to the result. This means that the type of demo would be str. To fix it, add an &:
let demo = &"demo"[..];
In your broader example, you are also running into the fact that you are creating an allocated String inside of the match statement (via join) and then attempting to return a reference to it. This is disallowed because the String will be dropped at the end of the match, invalidating any references. In another language, this could lead to memory unsafety.
One potential fix is to store the created String for the duration of the function, preventing its deallocation until after the new string is created:
fn choose(s: &str, pad: u8) -> String {
let tmp;
match pad {
0 => {
tmp = ["000000000000000", s].join("");
&tmp[s.len()..]
}
1 => {
tmp = [s, "000000000000000"].join("");
&tmp[..16]
}
_ => {
tmp = ["00", s, "0000000000000"].join("");
&tmp[..16]
}
}.to_string()
}
Editorially, there's probably more efficient ways of writing this function. The formatting machinery has options for padding strings. You might even be able to just truncate the string returned from join without creating a new one.
What it means is harder to explain succinctly. Rust has a number of types that are unsized. The most prevalent ones are str and [T]. Contrast these types to how you normally see them used: &str or &[T]. You might even see them as Box<str> or Arc<[T]>. The commonality is that they are always used behind a reference of some kind.
Because these types don't have a size, they cannot be stored in a variable on the stack — the compiler wouldn't know how much stack space to reserve for them! That's the essence of the error message.
See also:
What is the return type of the indexing operation?
Return local String as a slice (&str)
Why your first FizzBuzz implementation may not work

How can I create a trait/type to unify iterating over some set of integers from either a Range or a Vec?

I need the trait XYZ to define a method that allows iterating over some set of integers. This set of integers is defined either by a backing Vec or by a Range<usize>. However, I run into various (lifetime or type) issues depending on how I define the XYZIterator type that is supposed to unify these Iterators over Vec/Range.
The backup solution would be to allocate and return Vecs, but I wondered whether there was a way without cloning/allocating memory.
type XYZIterator = Box<dyn Iterator<Item = usize>>;
trait XYZ {
fn stuff(&self) -> XYZIterator;
}
struct Test {
objects: Vec<usize>,
}
impl XYZ for Test {
fn stuff(&self) -> XYZIterator {
Box::new(self.objects.iter())
}
}
struct Test2 {}
impl XYZ for Test2 {
fn stuff(&self) -> XYZIterator {
Box::new((1..4).into_iter())
}
}
fn main() {
let t1 = Test {
objects: vec![1, 2, 3],
};
let t2 = Test2 {};
t1.stuff().for_each(|x| println!("{}", x));
t2.stuff().for_each(|x| println!("{}", x));
t1.stuff()
.filter(|x| x % 2 == 0)
.for_each(|x| println!("{}", x));
t2.stuff()
.filter(|x| x % 2 == 0)
.for_each(|x| println!("{}", x));
}
error[E0271]: type mismatch resolving `<std::slice::Iter<'_, usize> as Iterator>::Item == usize`
--> src/main.rs:12:9
|
12 | Box::new(self.objects.iter())
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `usize`, found reference
|
= note: expected type `usize`
found reference `&usize`
= note: required for the cast to the object type `dyn Iterator<Item = usize>`
Your code has two issues:
In the implementation of XYZ for Test1, you return the iterator self.objects.iter(). Vec::iter iterates over references to the objects, not objects themselves, so this is an iterator over &usize, which doesn't match the return type. You should have gotten an error about this. It's easy to fix though: self.objects.iter().copied() will copy each element out of the reference.
In type XYZIterator = Box<dyn Iterator<Item = usize>>;, since there is no lifetime in the trait object, it defaults to 'static - that is, your iterator can live forever. But that's not the case with the vector iterator - it has a reference to the vector is iterating over. This is where you are having lifetime issues.
The solution is to give the XYZIterator type a lifetime:
type XYZIterator<'a> = Box<dyn Iterator<Item = usize> + 'a>;
And alter the traits and trait implementations to use the lifetime.
Also consider altering your type or function to accept any T: Iterator<Item=usize>; it will then accept any iterator that produces usizes

Error with non matching type: Option<&[u8]> and Option<&[u8;32]>

I playing with the Rust code, and I've got to a place in which I have a problem with converting Option<&[u8; 32]> to Option<&[u8]>.
A (very) simplified example:
pub type Foo = [u8; 32];
fn fun_one(inp: Option<&[u8]>) {
println!("{:?}", inp);
}
fn fun_two(x: Option<&Foo>) {
fun_one(x)
}
fn main() {
let x = [11u8; 32];
fun_two(Some(&x));
}
Link: Rust Playground
error[E0308]: mismatched types
--> src/main.rs:8:13
|
8 | fun_one(x)
| ^ expected slice `[u8]`, found array `[u8; 32]`
|
= note: expected enum `Option<&[u8]>`
found enum `Option<&[u8; 32]>`
A slice isn't just a pointer over an array. It's both the pointer to the data and a length (see Arrays and Slices) as it refers to only a part of the array. This is why the types aren't compatible.
What you want here is a slice over the whole array, which you get with the .. full range expression: slice = &array[..].
Having an option, you can conditionnaly apply this transformation using map.
Combining all this:
fn fun_two(x: Option<&Foo>) {
fun_one(x.map(|a| &a[..]))
}

What do I have to do to solve a "use of moved value" error?

I'm trying to compute the 10,001st prime in Rust (Project Euler 7), and as a part of this, my method to check whether or not an integer is prime references a vector:
fn main() {
let mut count: u32 = 1;
let mut num: u64 = 1;
let mut primes: Vec<u64> = Vec::new();
primes.push(2);
while count < 10001 {
num += 2;
if vectorIsPrime(num, primes) {
count += 1;
primes.push(num);
}
}
}
fn vectorIsPrime(num: u64, p: Vec<u64>) -> bool {
for i in p {
if num > i && num % i != 0 {
return false;
}
}
true
}
When I try to reference the vector, I get the following error:
error[E0382]: use of moved value: `primes`
--> src/main.rs:9:31
|
9 | if vectorIsPrime(num, primes) {
| ^^^^^^ value moved here, in previous iteration of loop
|
= note: move occurs because `primes` has type `std::vec::Vec<u64>`, which does not implement the `Copy` trait
What do I have to do to primes in order to be able to access it within the vectorIsPrime function?
With the current definition of your function vectorIsPrime(), the function specifies that it requires ownership of the parameter because you pass it by value.
When a function requires a parameter by value, the compiler will check if the value can be copied by checking if it implements the trait Copy.
If it does, the value is copied (with a memcpy) and given to the function, and you can still continue to use your original value.
If it doesn't, then the value is moved to the given function, and the caller cannot use it afterwards
That is the meaning of the error message you have.
However, most functions do not require ownership of the parameters: they can work on "borrowed references", which means they do not actually own the value (and cannot for example put it in a container or destroy it).
fn main() {
let mut count: u32 = 1;
let mut num: u64 = 1;
let mut primes: Vec<u64> = Vec::new();
primes.push(2);
while count < 10001 {
num += 2;
if vector_is_prime(num, &primes) {
count += 1;
primes.push(num);
}
}
}
fn vector_is_prime(num: u64, p: &[u64]) -> bool {
for &i in p {
if num > i && num % i != 0 {
return false;
}
}
true
}
The function vector_is_prime() now specifies that it only needs a slice, i.e. a borrowed pointer to an array (including its size) that you can obtain from a vector using the borrow operator &.
For more information about ownership, I invite you to read the part of the book dealing with ownership.
Rust is, as I would say, a “value-oriented” language. This means that if you define primes like this
let primes: Vec<u64> = …
it is not a reference to a vector. It is practically a variable that stores a value of type Vec<u64> just like any u64 variable stores a u64 value. This means that if you pass it to a function defined like this
fn vec_is_prime(num: u64, vec: Vec<u64>) -> bool { … }
the function will get its own u64 value and its own Vec<u64> value.
The difference between u64 and Vec<u64> however is that a u64 value can be easily copied to another place while a Vec<u64> value can only move to another place easily. If you want to give the vec_is_prime function its own Vec<u64> value while keeping one for yourself in main, you have to duplicate it, somehow. That's what's clone() is for. The reason you have to be explicit here is because this operation is not cheap. That's one nice thing about Rust: It's not hard to spot expensive operations. So, you could call the function like this
if vec_is_prime(num, primes.clone()) { …
but that's not really what you want, actually. The function does not need its own a Vec<64> value. It just needs to borrow it for a short while. Borrowing is much more efficient and applicable in this case:
fn vec_is_prime(num: u64, vec: &Vec<u64>) -> bool { …
Invoking it now requires the “borrowing operator”:
if vec_is_prime(num, &primes) { …
Much better. But we can still improve it. If a function wants to borrow a Vec<T> just for the purpose of reading it, it's better to take a &[T] instead:
fn vec_is_prime(num: u64, vec: &[u64]) -> bool { …
It's just more general. Now, you can lend a certain portion of a Vec to the function or something else entirely (not necessarily a Vec, as long as this something stores its values consecutively in memory, like a static lookup table). What's also nice is that due to coersion rules you don't need to alter anything at the call site. You can still call this function with &primes as argument.
For String and &str the situation is the same. String is for storing string values in the sense that a variable of this type owns that value. &str is for borrowing them.
You move value of primes to the function vectorIsPrime (BTW Rust use snake_case by convention). You have other options, but the best one is to borrow vector instead of moving it:
fn vector_is_prime(num: u64, p: &Vec<u64>) -> bool { … }
And then passing reference to it:
vector_is_prime(num, &primes)

Resources