3

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]?

Ömer Erden
  • 7,680
  • 5
  • 36
  • 45
Siguza
  • 21,155
  • 6
  • 52
  • 89
  • Your first thought is correct, but at the end it will not work unless `arr` has static lifetime, if it is a constant value for your function you can use it like this([Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bcfd843ccf2ca8018c74435345102b77)) otherwise it is not possible because `arr` is going to be dropped at the end of function's scope, any reference of `arr` is going to be a dangling pointer. – Ömer Erden Jul 23 '19 at 08:25

1 Answers1

2

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:

Stargateur
  • 24,473
  • 8
  • 65
  • 91
  • Well, I ended up creating my own struct, containing a `Box<[u8]>` and implementing `Read` manually. Thanks a bunch. :) – Siguza Jul 24 '19 at 03:34