Может ли const fn в rust объединять фрагменты байтов?

Когда я компилирую следующий фрагмент кода:

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]);

Я получаю следующую ошибку компилятора:

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

Ошибка очевидна: изменяемые ссылки в const fn еще не являются частью стабильной версии Rust. Но, может быть, есть способ добиться этого в стабильной версии Rust без использования изменяемых ссылок?

Я знаю, что мог бы сделать это вместо этого:

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

Но в данном случае я не использую константу AA повторно, чего я и пытаюсь добиться.

Спасибо за любую помощь в этом вопросе!


person Félix Poulin-Bélanger    schedule 10.10.2020    source источник


Ответы (1)


Вы можете использовать литерал массива и указать значения следующим образом:

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)
    }
}
person Peter Hall    schedule 10.10.2020
comment
Это чисто стилистически, но вы можете сделать это немного более кратким и (IMO) читаемым с шаблоном для параметра, вот так. - person apetranzilla; 10.10.2020
comment
Спасибо Питер! Это хорошо работает, и стилистическое предложение, сделанное @apetranzilla, приятно. Но допустим, я хочу обработать пакет произвольной длины, возможно ли это без const generic? Например, с определением структуры, например: struct Packet(&'static [u8]);, и конструктором, например const fn from(labels: &[&[u8; 2]]) -> Packet. - person Félix Poulin-Bélanger; 10.10.2020
comment
@FélixPoulin-Bélanger Я думаю, что прямо сейчас для этого вам понадобятся макросы. - person Peter Hall; 10.10.2020
comment
@FélixPoulin-Bélanger вот пример того, как макрос может использоваться для объединения литеральных массивов произвольной длины, как указано выше. - person apetranzilla; 11.10.2020
comment
@apetranzilla Поправьте меня, если я ошибаюсь (я пользователь макросов, а не разработчик макросов), но проблема с макросом concat! и вашим макросом concat_arrays! заключается в том, что они работают только с литералами. Я не мог определить const AA: [u8; 2] = [65, 65] и использовать AA в качестве аргумента макроса. Как я объяснил в вопросе, меня действительно волнует повторное использование одних и тех же констант. Лучшее (но несовершенное) решение, которое я нашел до сих пор, — это хранить слишком большой массив и длину в структуре Packet, как описано здесь @ 2e71828 (users.rust-lang.org/t/can-const-fn-concatenate-byte-slices/). - person Félix Poulin-Bélanger; 12.10.2020
comment
@ FélixPoulin-Bélanger да, это правильно. Этот макрос не делает то, что вам нужно. - person Peter Hall; 12.10.2020