6

When I compile the following code snippet:

struct Packet([u8; 4]);

impl Packet {
    const fn from(labels: [&[u8; 2]; 2]) -> Packet {
        let mut bytes = [0; 4];
        bytes[..2].copy_from_slice(labels[0]);
        bytes[2..].copy_from_slice(labels[1]);
        Packet(bytes)
    }
}

const AA: &[u8; 2] = b"AA";
const BB: &[u8; 2] = b"BB";
const CC: &[u8; 2] = b"CC";

const AABB: Packet = Packet::from([AA, BB]);
const AACC: Packet = Packet::from([AA, CC]);

I get the following compiler error:

error[E0723]: mutable references in const fn are unstable
 --> src/main.rs:7:9
  |
7 |         bytes[..2].copy_from_slice(labels[0]);
  |         ^^^^^^^^^^
  |
  = note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
  = help: add `#![feature(const_fn)]` to the crate attributes to enable

The error is very clear: mutable references in const fn are not yet part of stable Rust. But maybe there's a way to achieve that in stable Rust without using mutable references?

I know I could do this instead:

const AABB: Packet = Packet(*b"AABB");
const AACC: Packet = Packet(*b"AACC");

But in this case, I'm not reusing the "AA" constant which is precisely what I'm trying to achieve.

Thanks for any help on this matter!

Peter Hall
  • 53,120
  • 14
  • 139
  • 204

1 Answers1

4

You can use an array literal and supply the values like so:

impl Packet {
    const fn from(labels: [&[u8; 2]; 2]) -> Packet {
        let bytes = [labels[0][0], labels[0][1], labels[1][0], labels[1][1]];
        Packet(bytes)
    }
}
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • 2
    It's purely stylistic, but you can make this a little bit more concise and (IMO) readable with a pattern for the parameter, [like this](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=19aa363ef746d00b075d94e6e5449272). – apetranzilla Oct 10 '20 at 19:01
  • Thanks Peter! That works well and the stylistic suggestion made by @apetranzilla is nice. But let's say I want to handle arbitrary-length packet, is that possible without `const generic`? For example, with a struct definition like:`struct Packet(&'static [u8]);` and a constructor like `const fn from(labels: &[&[u8; 2]]) -> Packet` – Félix Poulin-Bélanger Oct 10 '20 at 19:09
  • 1
    @FélixPoulin-Bélanger I think, right now, you'd need macros for that. – Peter Hall Oct 10 '20 at 19:24
  • 1
    @FélixPoulin-Bélanger [Here's an example](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=dfd2041128caa77a6f301666b5d9a5bb) of how a macro can be used to concatenate arbitrary-length literal arrays as mentioned above – apetranzilla Oct 11 '20 at 01:43
  • @apetranzilla Correct me if I'm wrong (I'm a macro user, not a macro developer), but the problem with the `concat!` macro and your `concat_arrays!` macro is that they only work with literals. I couldn't define `const AA: [u8; 2] = [65, 65]` and use `AA` as a macro argument. As I explained in the question, I really care about reusing the same constants. The best (but imperfect) solution that I found so far is to store an oversized array and a length in the `Packet` struct, as described by @ 2e71828 here (https://users.rust-lang.org/t/can-const-fn-concatenate-byte-slices/49946/7). – Félix Poulin-Bélanger Oct 12 '20 at 17:40
  • @FélixPoulin-Bélanger yes, that is correct. This macro doesn't do what you need. – Peter Hall Oct 12 '20 at 19:07