2

I have a program where I need to append two Vec<u8> before they are are serialized.

Just to be sure how to do it, I made this example program:

let a: Vec<u8> = vec![1, 2, 3, 4, 5, 6];
let b: Vec<u8> = vec![7, 8, 9];
let c = [a, b].concat();
println!("{:?}", c);

Which works perfectly. The issue is now when I have to implement it in my own project. Here I need to write a function, the function takes a struct as input that looks like this:

pub struct Message2 {
    pub ephemeral_key_r: Vec<u8>,
    pub c_r: Vec<u8>,
    pub ciphertext2: Vec<u8>,
}

and the serialalization function looks like this:

pub fn serialize_message_2(msg: &Message2) -> Result<Vec<u8>> {
    let c_r_and_ciphertext = [msg.c_r, msg.ciphertext2].concat();
    let encoded = (
        Bytes::new(&msg.ephemeral_key_r),
        Bytes::new(&c_r_and_ciphertext),
    );
    Ok(cbor::encode_sequence(encoded)?)
}

The first issue that arises here is that it complains that msg.ciphertext2 and msg.c_r are moved values. This makes sense, so I add an & in front of both of them.

However, when I do this, the call to concat() fails, with this type error:

util.rs(77, 59): method cannot be called on `[&std::vec::Vec<u8>; 2]` due to unsatisfied trait bounds

So, when I borrow the values, then the expression [&msg.c_r, &msg.ciphertext2] becomes an array of two vec's, which there is not a concat() defined for.

I also tried calling clone on both vectors:

let c_r_and_ciphertext = [msg.c_r.clone(), msg.ciphertext2.clone()].concat();

and this actually works out!

But now I'm just wondering, why does borrowing the values change the types? and is there any things to think about when slapping on clone to values that are moved, and where I cannot borrow for some reason?

Caesar
  • 6,733
  • 4
  • 38
  • 44
Grazosi
  • 603
  • 1
  • 10
  • Does this answer your question? [How to concatenate immutable vectors in one line?](https://stackoverflow.com/questions/53469274/how-to-concatenate-immutable-vectors-in-one-line) – Caesar Feb 28 '22 at 00:54
  • it does touch on the same topic, but does not go into the exact reasons why clone works while the other attempts did not – Grazosi Feb 28 '22 at 12:14

2 Answers2

1

The reasons on why .concat() behaves as it does are a bit awkward.

To be able to call .concat(), the Concat trait must be implemented. It is implemented on slices of strings, and slices of V, where V can be Borrowed as slices of copyable T.

First, you're calling concat on an array, not a slice. However, auto-borrowing and unsize coercion are applied when calling a function with .. This turns the [V; 2] into a &[V] (where V = Vec<u8> in the working case and V = &Vec<u8> in the non-workin case). Try calling Concat::concat([a, b]) and you'll notice the difference.

So now is the question whether V can be borrowed as/into some &[T] (where T = u8 in your case). Two possibilities exist:

  • There is an impl<T> Borrow<[T]> for Vec<T>, so Vec<u8> can be turned into &[u8].
  • There is an impl<'_, T> Borrow<T> for &'_ T, so if you already have a &[u8], that can be used.

However, there is no impl<T> Borrow<[T]> for &'_ Vec<T>, so concatting [&Vec<_>] won't work.

So much for the theory, on the practical side: You can avoid the clones by using [&msg.c_r[..], &msg.ciphertext2[..]].concat(), because you'll be calling concat on &[&[u8]]. The &x[..] is a neat trick to turn the Vecs into slices (by slicing it, without slicing anything off…). You can also do that with .borrow(), but that's a bit more awkward, since you may need an extra type specification: [msg.c_r.borrow(), msg.ciphertext2.borrow()].concat::<u8>()

Caesar
  • 6,733
  • 4
  • 38
  • 44
0

I tried to reproduce your error message, which this code does:

fn main() {
    let a = vec![1, 2];
    let b = vec![3, 4];
    println!("{:?}", [&a, &b].concat())
}

gives:

error[E0599]: the method `concat` exists for array `[&Vec<{integer}>; 2]`, but its trait bounds were not satisfied
 --> src/main.rs:4:31
  |
4 |     println!("{:?}", [&a, &b].concat())
  |                               ^^^^^^ method cannot be called on `[&Vec<{integer}>; 2]` due to unsatisfied trait bounds
  |
  = note: the following trait bounds were not satisfied:
          `[&Vec<{integer}>]: Concat<_>`

It is a simple matter of helping the compiler to see that &a works perfectly fine as a slice, by calling it &a[..]:

fn main() {
    let a = vec![1, 2];
    let b = vec![3, 4];
    println!("{:?}", [&a[..], &b[..]].concat())
}

why does borrowing the values change the types?

Borrowing changes a type into a reference to that same type, so T to &T. These types are related, but are not the same.

is there any things to think about when slapping on clone to values that are moved, and where I cannot borrow for some reason?

Cloning is a good way to sacrifice performance to make the borrow checker happy. It (usually) involves copying the entire memory that is cloned, but if your code is not performance critical (which most code is not), then it may still be a good trade-off...

hkBst
  • 2,818
  • 10
  • 29