I have written some tests where I need to assert that two arrays are equal. Some arrays are [u8; 48] while others are [u8; 188]:
#[test]
fn mul() {
let mut t1: [u8; 48] = [0; 48];
let t2: [u8; 48] = [0; 48];
// some computation goes here.
assert_eq!(t1, t2, "\nExpected\n{:?}\nfound\n{:?}", t2, t1);
}
I get multiple errors here:
error[E0369]: binary operation `==` cannot be applied to type `[u8; 48]`
--> src/main.rs:8:5
|
8 | assert_eq!(t1, t2, "\nExpected\n{:?}\nfound\n{:?}", t2, t1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: an implementation of `std::cmp::PartialEq` might be missing for `[u8; 48]`
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error[E0277]: the trait bound `[u8; 48]: std::fmt::Debug` is not satisfied
--> src/main.rs:8:57
|
8 | assert_eq!(t1, t2, "\nExpected\n{:?}\nfound\n{:?}", t2, t1);
| ^^ `[u8; 48]` cannot be formatted using `:?`; if it is defined in your crate, add `#[derive(Debug)]` or manually implement it
|
= help: the trait `std::fmt::Debug` is not implemented for `[u8; 48]`
= note: required by `std::fmt::Debug::fmt`
Trying to print them as slices like t2[..] or t1[..] doesn't seem to work.
How do I use assert with these arrays and print them?
For the comparison part you can just convert the arrays to iterators and compare elementwise.
assert_eq!(t1.len(), t2.len(), "Arrays don't have the same length");
assert!(t1.iter().zip(t2.iter()).all(|(a,b)| a == b), "Arrays are not equal");
With Iterator::eq, it is possible to compare anything that can be turned into an iterator for equality:
let mut t1: [u8; 48] = [0; 48];
let t2: [u8; 48] = [0; 48];
assert!(t1.iter().eq(t2.iter()));
Using slices
As a workaround, you can just use &t1[..] (instead of t1[..]) to make arrays into slices. You'll have to do this for both comparison and formatting.
assert_eq!(&t1[..], &t2[..], "\nExpected\n{:?}\nfound\n{:?}", &t2[..], &t1[..]);
or
assert_eq!(t1[..], t2[..], "\nExpected\n{:?}\nfound\n{:?}", &t2[..], &t1[..]);
Formatting arrays directly
Ideally, the original code should compile, but it doesn't for now. The reason is that the standard library implements common traits (such as Eq and Debug) for arrays of only up to 32 elements, due to lack of const generics.
Therefore, you can compare and format shorter arrays like:
let t1: [u8; 32] = [0; 32];
let t2: [u8; 32] = [1; 32];
assert_eq!(t1, t2, "\nExpected\n{:?}\nfound\n{:?}", t2, t1);
You could make Vecs out of them.
fn main() {
let a: [u8; 3] = [0, 1, 2];
let b: [u8; 3] = [2, 3, 4];
let c: [u8; 3] = [0, 1, 2];
let va: Vec<u8> = a.to_vec();
let vb: Vec<u8> = b.to_vec();
let vc: Vec<u8> = c.to_vec();
println!("va==vb {}", va == vb);
println!("va==vc {}", va == vc);
println!("vb==vc {}", vb == vc);
}
Related
I'm trying to do this, but doesn't work:
fn foo() {
let v = b"Hello".to_vec();
let a = v.as_bytes();
}
I'm getting:
error[E0599]: no method named `as_bytes` found for struct `Vec<u8>` in the current scope
--> foo
|
26 | let a = v.as_bytes();
| ^^^^^^^^ method not found in `Vec<u8>`
If you don't wish to write the [u8; N] type, you can choose not '.to_vec()' in the beginning and use *b"string" to obtain the byte array directly without type annotation.
struct Pack {
data: Box<[u8]>,
}
fn foo() {
let mut p = Pack {
data: Box::new(*b"Hello!"),
};
p.data = Box::new(*b"Bye!");
}
#[test]
fn test() {
let v = *b"Hello!";
let boxed_v = Box::new(*b"Hello!");
assert_eq!(
format!("{} {} {} {} {}", v[0], v[1], v[2], v[3], v[4]),
"72 101 108 108 111"
);
assert_eq!(
format!(
"{} {} {} {} {}",
boxed_v[0], boxed_v[1], boxed_v[2], boxed_v[3], boxed_v[4]
),
"72 101 108 108 111"
)
}
In Rust, an array has its length encoded in its type – e.g., [u8; 5] – it is a compile-time property, whereas a Vec's length is a run-time property. Every byte array, [u8; N], implements TryFrom<Vec<u8>> , so Vec<u8> implements TryInto<[u8; N]> as a result. Therefore, you can use try_into() on a Vec<u8> to convert it into a byte array:
let a: [u8; 5] = v.try_into().unwrap();
Note that TryInto::try_into() returns a Result for the reason explained above: the mismatch of the nature of the length property of a Vec and an array – run-time and compile-time, respectively.
Why not convert the Vec<u8> into a byte slice instead?
Keep in mind that you can easily create a byte slice (i.e., &[u8]) from a Vec<T>. A Vec<u8> deref coerces into [u8] as it implements Deref<Target=[u8]>:
let v = b"Hello".to_vec();
let a: &[u8] = &v;
You can also call as_slice() on a Vec<u8>:
let a = v.as_slice();
This may be what you want because you are probably using a Vec to be able to change the length at run time.
Here is one way to covert a vector of bytes to a byte array:
use std::convert::TryInto;
fn main() {
let v: Vec<u8> = vec![44u8, 99u8];
println!("{v:?}");
let a: [u8; 2] = v.try_into().unwrap();
println!("{a:?}");
}
The size of the array needs to be known at compile time though.
How can I make use of functional patterns without incurring in borrowing problems? The solution proposed by the compiler leads to another error (expected array [u8; 3], found '&[u8]') and it goes on from one error to another different error, indefinitely.
Some related code seem overly complicated for such a simple task like in this other question.
use reduce::Reduce;
/// Take an array representing a sequence of 3-tuples and fold it through an arbitrary sandwich logic.
fn sandwich(lst: &[u8])->[u8; 3]{
lst.chunks(3).reduce(|x, y| [x[0], y[1], x[0]]).unwrap()
}
/*
3 | lst.chunks(3).reduce(|x, y| [x[0], y[1], x[0]]).unwrap()
| ^^^^^^^^^^^^^^^^^^
| |
| expected `&[u8]`, found array `[u8; 3]`
| help: consider borrowing here: `&[x[0], y[1], x[0]]`
*/
The best compilable code I could write was this convoluted one, giving up reduce at all:
fn sandwich2(lst: &[u8])->[u8; 3]{
let mut r: [u8; 3] = lst[..].try_into().unwrap();
for i in (3..lst.len()).step_by(3) {
let y = &lst[i..i + 3];
r = [r[0], y[1], r[0]];
}
r
}
Please note that sandwich is just an example to illustrate the problem (that does nothing actually smart). I expect an external much more complex function instead of that lambda.
You have to somehow take ownership of the values into a required [u8; 3].
Maybe as this example using the iterator_fold_self feature (as per today nightly):
#![feature(iterator_fold_self)]
/// Take an array representing a sequence of 3-tuples and reduce it through an arbitrary sandwich logic.
fn sandwich(lst: &[u8]) -> [u8; 3] {
lst.chunks(3)
.map(|x| [x[0], x[1], x[2]])
.reduce(|x, y| [x[0], y[1], x[0]])
.unwrap()
}
fn main() {
let test_set = [1, 2, 3, 1, 2, 3];
println!("{:?}", sandwich(&test_set));
}
Playground
You can use try_into(from this famous answer) to get an owned slice:
fn sandwich(lst: &[u8]) -> [u8; 3] {
lst.chunks(3)
.map(|x| -> [u8; 3] { x.try_into().unwrap() } )
.reduce(|x, y| [x[0], y[1], x[0]])
.unwrap()
}
Playground
I have a Reader that I want to prepend some bytes to, creating a Chain. Ideally I'd want to do this:
use std::io::{Chain, Read};
fn thingify<R: Read>(r: R) -> Chain<[u8; 3], R> {
let mut arr = [1u8, 2u8, 3u8];
// Modify arr here
return arr.chain(r);
}
But that throws a compiler error:
error[E0308]: mismatched types
--> test.rs:7:12
|
3 | fn thingify<R: Read>(r: R) -> Chain<[u8; 3], R>
| ----------------- expected `std::io::Chain<[u8; 3], R>` because of return type
...
7 | return arr.chain(r);
| ^^^^^^^^^^^^ expected array of 3 elements, found &[u8]
|
= note: expected type `std::io::Chain<[u8; 3], _>`
found type `std::io::Chain<&[u8], _>`
From what I understand, this seems to be because Read is implemented for slices rather than arrays, and somehow my array decays to a slice here.
But when I change the array in the return type to a slice and give it an explicit lifetime like so:
use std::io::{Chain, Read};
fn thingify<'a, R: Read>(r: R) -> Chain<&'a [u8], R> {
let arr = [1u8, 2u8, 3u8];
// Modify arr here
return arr.chain(r);
}
I just get another compiler error instead:
error[E0515]: cannot return value referencing local variable `arr`
--> test.rs:19:12
|
19 | return arr.chain(r);
| ---^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `arr` is borrowed here
How can I transfer ownership of my array to the Chain so that I can return it? Is that simply not doable with a [u8]?
Because Read is implemented for &'_ [u8] but not for [u8; 3], the compiler automatically converts your array into a reference slice. This mean your array must be valid as long as the slice live so as long as the Chain live.
There are several solutions, you could ask to the caller a mutable slice, you could make it static if you want to be able to mutate it, if you don't you can make it const, if you need to resize it you need a Vec, etc...
use std::io::{stdin, Chain, Read};
fn a<R: Read>(arr: &mut [u8; 3], r: R) -> Chain<&[u8], R> {
arr.copy_from_slice(&[1, 2, 3]);
arr.chain(r)
}
fn b<R: Read>(r: R) -> Chain<&'static [u8], R> {
const ARR: [u8; 3] = [1, 2, 3];
ARR.chain(r)
}
fn main() {
let mut arr = [0; 3];
println!("{:?}", a(&mut arr, stdin()));
println!("{:?}", b(stdin()));
}
See:
Is there any way to return a reference to a variable created in a function?
I have this toy example, but it's what I'm trying to accomplish:
fn lazy_vec() {
let vec: Vec<i64> = vec![1, 2, 3, 4, 5];
let mut iter: Box<Iterator<Item = i64>> = Box::new(vec.into_iter());
iter = Box::new(iter.map(|x| x + 1));
// potentially do additional similar transformations to iter
println!("{:?}", iter.collect::<Vec<_>>());
}
This (if I'm not mistaken) is a lazy iterator pattern, and the actual map operation doesn't occur until .collect() is called. I want to do the same thing with slices:
fn lazy_slice() {
let vec: Vec<i64> = vec![1, 2, 3, 4, 5];
let slice: &[i64] = &vec[..3];
let mut iter: Box<Iterator<Item = i64>> = Box::new(slice.into_iter());
iter = Box::new(iter.map(|x| x + 1));
// potentially do additional similar transformations to iter
println!("{:?}", iter.collect::<Vec<_>>());
}
This results in a type mismatch:
error[E0271]: type mismatch resolving `<std::slice::Iter<'_, i64> as std::iter::Iterator>::Item == i64`
--> src/main.rs:4:47
|
4 | let mut iter: Box<Iterator<Item = i64>> = Box::new(slice.into_iter());
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found i64
|
= note: expected type `&i64`
found type `i64`
= note: required for the cast to the object type `std::iter::Iterator<Item=i64>`
I can't figure out what I need to do to resolve this error. The second note made me think I needed:
iter = Box::new(iter.map(|x| x + 1) as Iterator<Item = i64>);
or
iter = Box::new(iter.map(|x| x + 1)) as Box<Iterator<Item = i64>>;
These fail with other errors depending on the exact syntax (e.g. expected reference, found i64, or expected i64, found &i64). I've tried other ways to declare the types involved, but I'm basically just blindly adding & and * in places and not making any progress.
What am I missing here? What do I need to change in order to make this compile?
Edit
Here's a slightly more concrete example - I need iter to be mut so that I can compose an unknown number of such transformations before actually invoking .collect(). My impression was this was a somewhat common pattern, apologies if that wasn't correct.
fn lazy_vec(n: i64) {
let vec: Vec<i64> = vec![1, 2, 3, 4, 5];
let mut iter: Box<Iterator<Item = i64>> = Box::new(vec.into_iter());
for _ in 0..n {
iter = Box::new(iter.map(|x| x + 1));
}
println!("{:?}", iter.collect::<Vec<_>>());
}
I'm aware I could rewrite this specific task in a simpler way (e.g. a single map that adds n to each element) - it's an oversimplified MCVE of the problem I'm running into. My issue is this works for lazy_vec, but I'm not sure how to do the same with slices.
Edit 2
I'm just learning Rust and some of the nomenclature and concepts are new to me. Here's what I'm envisioning doing in Python, for comparison. My intent is to do the same thing with slices that I can currently do with vectors.
#!/usr/bin/env python3
import itertools
ls = [i for i in range(10)]
def lazy_work(input):
for i in range(10):
input = (i + 1 for i in input)
# at this point no actual work has been done
return input
print("From list: %s" % list(lazy_work(ls)))
print("From slice: %s" % list(lazy_work(itertools.islice(ls, 5))))
Obviously in Python there's no issues with typing, but hopefully that more clearly demonstrates my intent?
As discussed in What is the difference between iter and into_iter?, these methods create iterators which yield different types when called on a Vec compared to a slice.
[T]::iter and [T]::into_iter both return an iterator which yields values of type &T. That means that the returned value doesn't implement Iterator<Item = i64> but instead Iterator<Item = &i64>, as the error message states.
However, your subsequent map statements change the type of the iterator's item to an i64, which means the type of the iterator would also need to change. As an analogy, you've essentially attempted this:
let mut a: &i64 = &42;
a = 99;
Iterator::cloned exists to make clones of the iterated value. In this case, it converts a &i64 to an i64 essentially dereferencing the value:
fn lazy_slice(n: i64) {
let array = [1i64, 2, 3, 4, 5];
let mut iter: Box<Iterator<Item = i64>> = Box::new(array.iter().cloned());
for _ in 0..n {
iter = Box::new(iter.map(|x| x + 1));
}
println!("{:?}", iter.collect::<Vec<_>>());
}
I want to take the x first and last elements from a vector and concatenate them. I have the following code:
fn main() {
let v = (0u64 .. 10).collect::<Vec<_>>();
let l = v.len();
vec![v.iter().take(3), v.iter().skip(l-3)];
}
This gives me the error
error[E0308]: mismatched types
--> <anon>:4:28
|
4 | vec![v.iter().take(3), v.iter().skip(l-3)];
| ^^^^^^^^^^^^^^^^^^ expected struct `std::iter::Take`, found struct `std::iter::Skip`
<anon>:4:5: 4:48 note: in this expansion of vec! (defined in <std macros>)
|
= note: expected type `std::iter::Take<std::slice::Iter<'_, u64>>`
= note: found type `std::iter::Skip<std::slice::Iter<'_, u64>>`
How do I get my vec of 1, 2, 3, 8, 9, 10? I am using Rust 1.12.
Just use .concat() on a slice of slices:
fn main() {
let v = (0u64 .. 10).collect::<Vec<_>>();
let l = v.len();
let first_and_last = [&v[..3], &v[l - 3..]].concat();
println!("{:?}", first_and_last);
// The output is `[0, 1, 2, 7, 8, 9]`
}
This creates a new vector, and it works with arbitrary number of slices.
(Playground link)
Ok, first of all, your initial sequence definition is wrong. You say you want 1, 2, 3, 8, 9, 10 as output, so it should look like:
let v = (1u64 .. 11).collect::<Vec<_>>();
Next, you say you want to concatenate slices, so let's actually use slices:
let head = &v[..3];
let tail = &v[l-3..];
At this point, it's really down to which approach you like the most. You can turn those slices into iterators, chain, then collect...
let v2: Vec<_> = head.iter().chain(tail.iter()).collect();
...or make a vec and extend it with the slices directly...
let mut v3 = vec![];
v3.extend_from_slice(head);
v3.extend_from_slice(tail);
...or extend using more general iterators (which will become equivalent in the future with specialisation, but I don't believe it's as efficient just yet)...
let mut v4: Vec<u64> = vec![];
v4.extend(head);
v4.extend(tail);
...or you could use Vec::with_capacity and push in a loop, or do the chained iterator thing, but using extend... but I have to stop at some point.
Full example code:
fn main() {
let v = (1u64 .. 11).collect::<Vec<_>>();
let l = v.len();
let head = &v[..3];
let tail = &v[l-3..];
println!("head: {:?}", head);
println!("tail: {:?}", tail);
let v2: Vec<_> = head.iter().chain(tail.iter()).collect();
println!("v2: {:?}", v2);
let mut v3 = vec![];
v3.extend_from_slice(head);
v3.extend_from_slice(tail);
println!("v3: {:?}", v3);
// Explicit type to help inference.
let mut v4: Vec<u64> = vec![];
v4.extend(head);
v4.extend(tail);
println!("v4: {:?}", v4);
}
You should collect() the results of the take() and extend() them with the collect()ed results of skip():
let mut p1 = v.iter().take(3).collect::<Vec<_>>();
let p2 = v.iter().skip(l-3);
p1.extend(p2);
println!("{:?}", p1);
Edit: as Neikos said, you don't even need to collect the result of skip(), since extend() accepts arguments implementing IntoIterator (which Skip does, as it is an Iterator).
Edit 2: your numbers are a bit off, though; in order to get 1, 2, 3, 8, 9, 10 you should declare v as follows:
let v = (1u64 .. 11).collect::<Vec<_>>();
Since the Range is left-closed and right-open.