синтаксис для объявления типа массива с переменным размером (известный во время компиляции)? - PullRequest
1 голос
/ 11 октября 2019

Я хотел бы создать структуру, имеющую байтовый массив, где конкретный экземпляр может иметь разные размеры в зависимости от создания структуры, известной во время компиляции.

Я создал надуманный примериспользуя структуру, которая имеет байтовое представление числа с плавающей запятой с отдельным полем типа. Ниже приведена рабочая реализация:

#![feature(float_to_from_bytes)]

#[derive(Debug)]
enum TypeMarker {
    NUMBER = 0x00,  // f64
    // BOOLEAN: u8 = 0x01, // bool
    // STRING: u8 = 0x02,  // UTF-8 string
}

#[derive(Debug)]
struct Value {
    t: TypeMarker,
    bytes: [u8; 8]
}

impl From<f64> for Value {
    fn from(v: f64) -> Self {
        Value {
            t: TypeMarker::NUMBER,
            bytes: v.to_be_bytes()
        }
    }
}

fn main() {
  let num = 4.0;
  println!("num = {:?}", num);

  let v1 = Value::from(4.0);
  println!("Value::from(4.0) = {:?}", v1);

  let v2:Value = num.into();
  println!("num.into() = {:?}", v2);

}

Этот рабочий пример (см. Также repo на github ) использует ночной ржавчину.

Запуск примера ... cargo +nightly run --example into

дает ожидаемый результат:

num = 4.0
Value::from(4.0) = Value { t: NUMBER, bytes: [64, 16, 0, 0, 0, 0, 0, 0] }
num.into() = Value { t: NUMBER, bytes: [64, 16, 0, 0, 0, 0, 0, 0] }

Однако я хочу поддерживать различные типы чиселгде размер известен во время компиляции. Чтобы проиллюстрировать этот вопрос, в приведенном ниже примере добавляется impl From<i32> (длина которого 4 байта):

#![feature(float_to_from_bytes)]

#[derive(Debug)]
enum TypeMarker {
    NUMBER = 0x00,  // f64
    // BOOLEAN: u8 = 0x01, // bool
    // STRING: u8 = 0x02,  // UTF-8 string
}

#[derive(Debug)]
struct Value {
    t: TypeMarker,
    bytes: [u8; 8]
}

impl From<f64> for Value {
    fn from(v: f64) -> Self {
        Value {
            t: TypeMarker::NUMBER,
            bytes: v.to_be_bytes()
        }
    }
}

impl From<i32> for Value {
    fn from(v: i32) -> Self {
        Value {
            t: TypeMarker::NUMBER,
            bytes: v.to_be_bytes()
        }
    }
}


fn main() {
  let num = 4.0;
  println!("num = {:?}", num);

  let v1 = Value::from(4.0);
  println!("Value::from(4.0) = {:?}", v1);

  let v2:Value = num.into();
  println!("num.into() = {:?}", v2);

}

это приводит к следующей ошибке

error[E0308]: mismatched types
  --> examples/into.rs:33:20
   |
33 |             bytes: v.to_be_bytes()
   |                    ^^^^^^^^^^^^^^^ expected an array with a fixed size of 8 elements, found one with 4 elements
   |
   = note: expected type `[u8; 8]`
              found type `[u8; 4]`

Я хотел бы объявить Value структура, так что он может быть создан с массивами байтов переменного размера (где размер известен во время компиляции).

я пробовал:

struct Value {
    t: TypeMarker,
    bytes: [u8; usize]
}
error[E0423]: expected value, found builtin type `usize`
  --> examples/into.rs:17:17
   |
17 |     bytes: [u8; usize]
   |                 ^^^^^ not a value

error[E0277]: arrays only have std trait implementations for lengths 0..=32
  --> examples/into.rs:17:5
   |
17 |     bytes: [u8; usize]
   |     ^^^^^^^^^^^^^^^^^^ the trait `std::array::LengthAtMost32` is not implemented for `[u8; _]`
   |
   = note: required because of the requirements on the impl of `std::fmt::Debug` for `[u8; _]`
   = note: required because of the requirements on the impl of `std::fmt::Debug` for `&[u8; _]`
   = note: required for the cast to the object type `dyn std::fmt::Debug`

ИтакЯ попытался:

struct Value {
    t: TypeMarker,
    bytes: [u8; _]
}

, который тоже не работал:

error: expected expression, found reserved identifier `_`
  --> examples/into.rs:17:17
   |
17 |     bytes: [u8; _]
   |                 ^ expected expression

error: aborting due to previous error

Кажется, это должно быть возможно, и я думаю, что однажды прочитал синтаксис для этого, но яЯ перечитал многие разделы Rust book , просмотрел десятки других постов и не могу понять синтаксис.

Вопрос: Как изменить объявление bytes, чтобы исправить приведенный выше пример, иллюстрирующий ошибку? И, если это не поддерживается или не является идиоматическим, какой подход будет работать?

1 Ответ

2 голосов
/ 11 октября 2019

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

Я предлагаю либо использовать Vec для поля байтов

#[derive(Debug)]
struct Value {
    t: TypeMarker,
    bytes: Vec<u8>
}

или для использования массива в штучной упаковке:

#![feature(float_to_from_bytes)]

use std::boxed::Box;

#[derive(Debug)]
enum TypeMarker {
    NUMBER = 0x00,  // f64
    // BOOLEAN: u8 = 0x01, // bool
    // STRING: u8 = 0x02,  // UTF-8 string
}

#[derive(Debug)]
struct Value {
    t: TypeMarker,
    bytes: Box<[u8]>,
}

impl From<f64> for Value{
    fn from(v: f64) -> Self {
        Value {
            t: TypeMarker::NUMBER,
            bytes: Box::new(v.to_be_bytes()),
        }
    }
}

impl From<i32> for Value{
    fn from(v: i32) -> Self {
        Value {
            t: TypeMarker::NUMBER,
            bytes: Box::new(v.to_be_bytes()),
        }
    }
}


fn main() {
  let num = 4.0;
  println!("num = {:?}", num);

  let v1 = Value::from(4.0);
  println!("Value::from(4.0) = {:?}", v1);

  let v2:Value = num.into();
  println!("num.into() = {:?}", v2);

}

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

Надеюсь, это поможет! Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...