Procedural macro to auto-generate match - rust

I am writing a toy virtual machine, where I need to decode instructions. I have an Instruction trait with some methods and concrete instructions implement this trait. Also, I have a decode function which just takes a byte and matches it to return a specific instruction corresponding to the byte given.
pub struct FirstInstruction { ... }
pub struct SecondInstruction { ... }
pub struct ThirdInstruction { ... }
pub fn decode(byte: u8) -> Box<dyn Instruction> {
match byte {
0x1 => Box::new(FirstInstruction::new()),
0x2 => Box::new(SecondInstruction::new()),
0x3 => Box::new(ThirdInstruction::new()),
}
}
I was wondering how to write a procedural macro that would allow me to auto-generate the decode function. At the same time, it will get the byte to match in its arguments like this:
#[instruction(0x1)]
pub struct FirstInstruction { ... }
#[instruction(0x2)]
pub struct SecondInstruction { ... }
#[instruction(0x3)]
pub struct ThirdInstruction { ... }
pub fn decode(byte: u8) -> Box<dyn Instruction> {
// Autogenerated
}
I understand that from the practical standpoint this whole thing can be useless. I am just curious about how it can be implemented using macros, if it is even possible.
I tried to read about how to write procedural macros, but I don't understand how one can accumulate information about all structs with the specific attribute and then generate a whole new function.

At the end of the day I found out that this is pretty hard to implement.
I decided to stick to a simple macro that would generate the decode function:
macro_rules! register_instructions {
{
$(
$code_value:expr => $struct_name:ty
),*
} => {
pub fn decode(code: &[u8]) -> Box<dyn Instruction> {
let instruction_code = code[0];
match instruction_code {
$($code_value => Box::new(<$struct_name>::new(code))),*,
_ => panic!("Invalid instruction"),
}
}
}
}
Now it looks like this:
register_instructions! {
0x01 => AddInstruction,
0x02 => SubInstruction,
0x03 => MulInstruction,
0x04 => DivInstruction,
0x05 => JumpInstruction,
0x06 => LoadInstruction,
0x07 => FinishInstruction,
0x08 => OutInstruction,
0x09 => EqualInstruction,
0x0A => LessInstruction,
0x0B => LessEqualInstruction,
0x0C => LoadAbsoluteInstruction
}

Related

Access the value of an enum variant

I am working on some language bindings to Arrayfire using the arrayfire-rust crate.
Arrayfire has a typed struct Array<T> which represents a matrix. All acceptable types implement the HasAfEnum trait. This trait has a number of associated types, whose values are not the same for the types that implement this trait.
Since I need a reference to the array in a Rwlock for safe language interop, I have defined the following struct:
pub struct ExAfRef(pub RwLock<ExAfArray>);
impl ExAfRef {
pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
Self(RwLock::new(ExAfArray::new(slice, dim, dtype)))
}
pub fn value(&self) -> ExAfArray {
match self.0.try_read() {
Ok(refer) => (*refer),
Err(_) => unreachable!(),
}
}
}
which is contained by a struct:
pub struct ExAf {
pub resource: ResourceArc<ExAfRef>,
}
impl ExAf {
pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
Self {
resource: ResourceArc::new(ExAfRef::new(slice, dim, dtype)),
}
}
// This function is broken
pub fn af_value<T: HasAfEnum>(&self) -> &Array<T> {
self.resource.value().value()
}
}
With the help of the following enum:
pub enum ExAfArray {
U8(Array<u8>),
S32(Array<i32>),
S64(Array<i64>),
F32(Array<f32>),
F64(Array<f64>),
}
impl ExAfArray {
pub fn new(slice: &[u8], dim: Dim4, dtype: ExAfDType) -> Self {
let array = Array::new(slice, dim);
match dtype {
ExAfDType::U8 => ExAfArray::U8(array),
ExAfDType::S32 => ExAfArray::S32(array.cast::<i32>()),
ExAfDType::S64 => ExAfArray::S64(array.cast::<i64>()),
ExAfDType::F32 => ExAfArray::F32(array.cast::<f32>()),
ExAfDType::F64 => ExAfArray::F64(array.cast::<f64>()),
}
}
// This function is broken
pub fn value<T: HasAfEnum>(&self) -> &Array<T> {
// match self {
// ExAfArray::U8(array) => array,
// ExAfArray::S32(array) => array,
// ExAfArray::S64(array) => array,
// ExAfArray::F32(array) => array,
// ExAfArray::F64(array) => array,
// }
if let ExAfArray::U8(array) = self {
return array;
} else if let ExAfArray::S32(array) = self {
return array;
} else if let ExAfArray::S64(array) = self {
return array;
} else if let ExAfArray::F32(array) = self {
return array;
} else {
let ExAfArray::F64(array) = self;
return array;
}
}
pub fn get_type(&self) -> ExAfDType {
match self {
ExAfArray::U8(array) => ExAfDType::U8,
ExAfArray::S32(array) => ExAfDType::S32,
ExAfArray::S64(array) => ExAfDType::S64,
ExAfArray::F32(array) => ExAfDType::F32,
ExAfArray::F64(array) => ExAfDType::F64,
}
}
}
I have used an enum because generic structs are not supported in my language-interop "framework" and because the HasAfEnum trait has associated types (hence dynamic dispatch using dyn is not viable (at least to my knowledge)).
This has worked fine for initializing new arrays.
However when I need to apply some operation on an array, I need to be able to access the value stored by the enum variant. However I am unable to write a type signature for a function to access the value, as dynamic dispatch is not usable and generics are too boilerplate.
Since all variants are tuples, is there some way I can access the value of the tuple variant using a built-in enum feature?
EDIT:
I am using rustler
In short, no there is not a way to do what you seem to be trying to do in Rust presently.
Your functions are broken because you are trying to use generics orthogonally to how they work. When a generic function is called in Rust, the caller fills in the type parameters, not the callee. However, your enum in a sense "knows" what the concrete array type is, so only it can determine what that type parameter is supposed to be. If this mismatch is blocking your progress, this usually calls for a reconsideration of your code structure.
This also explains why there is no built-in enum method that does what you're trying to do. That method would run into the same issue as your value method. When you want to inspect the contents of an enum in Rust, you need to pattern match on it.
There is at least one way to try to accomplish your goal, but I would not really recommend it. One change that makes the code closer to being viable is by passing a closure into the function to make the modification, (the syntax below is not currently valid Rust but it gets the idea across):
pub fn modify<'a, F>(&'a self, op: F)
where
F: for<T: HasAfEnum> FnOnce(&'a Array<T>)
{
// This looks repetitive, but the idea is that in each branch
// the type parameter T takes on the appropriate type for the variant
match self {
ExAfArray::U8(array) => op(array),
ExAfArray::S32(array) => op(array),
ExAfArray::S64(array) => op(array),
ExAfArray::F32(array) => op(array),
ExAfArray::F64(array) => op(array),
}
}
Unfortunately the for<T> FnTrait(T) syntax does not exist yet and I'm not even sure if there's a proposal for it to be added. This can be worked around through a macro:
pub(crate) fn call_unary<F, T, U>(arg: T, f: F) -> U
where F: FnOnce(T) -> U {
f(arg)
}
macro_rules! modify {
($ex_af_array:expr, $op:expr) => {
match &$ex_af_array {
ExAfArray::U8(array) => call_unary(array, $op),
ExAfArray::S32(array) => call_unary(array, $op),
ExAfArray::S64(array) => call_unary(array, $op),
ExAfArray::F32(array) => call_unary(array, $op),
ExAfArray::F64(array) => call_unary(array, $op),
}
};
}
The call_unary helper is needed to ensure type inference works properly. ($op)(array) will fail to compile when the types of the arguments to $op need to be inferred.
Now this solution mostly covers the functionality that for<T> FnTrait(T) would provide, but it's not very clean code (especially after the macro body is sanitized), and the compiler errors will be poor if the macro is misused.

Stack overflow issue in rust when making a simple VM

I have come to you guys today with an error I cannot seem to fix sadly.
First let me explain what I'm trying to do. I am writing a little VM in Rust. I have just finished writing the bare minimum for the VM as you can tell by how unfinished the code is. I have made a system where you can load the program into a certain spot in memory so you can jump to that spot for subroutines later on. The run function in the Arcate struct runs whatever operation is on 0x0000.
As you can also see I have gone with using a databuss just so I can use external memory devices like creating another ArcateMemory struct as a different "drive".
It seems I am getting a stack overflow but since Rust has the best stack overflow messages all I am getting is that it is in main.
Thanks again for your help. Sorry if it is a stupid mistake I'm a little new to Rust.
main.rs
#![allow(dead_code)]
type Instr = u8;
type Program = Vec<Instr>;
#[derive(PartialEq, Eq)]
enum Signals {
BusWrite,
BusRead,
ErrNoInstruction,
Success,
Halt,
}
struct Arcate {
mem : ArcateMem,
ci: Instr,
pc: i32,
acr: i32,
gp1: i32,
gp2: i32,
gp3: i32,
gp4: i32,
gp5: i32,
gp6: i32,
gp7: i32,
gp8: i32,
}
impl Arcate {
fn new(memory: ArcateMem) -> Self {
Arcate {
mem: memory,
ci: 0,
pc: 0,
acr: 0,
gp1: 0,
gp2: 0,
gp3: 0,
gp4: 0,
gp5: 0,
gp6: 0,
gp7: 0,
gp8: 0,
}
}
fn fetchi(&mut self) {
let i: Instr = ArcateBus {data: 0, addr: self.pc, todo: Signals::BusRead, mem: self.mem}.exec();
self.ci = i;
self.pc += 1;
}
fn fetcha(&mut self) -> Instr{
let i: Instr = ArcateBus {data: 0, addr: self.pc, todo: Signals::BusRead, mem: self.mem}.exec();
self.pc += 1;
i
}
fn getr(&mut self, reg: Instr) -> i32 {
match reg {
0x01 => { self.ci as i32},
0x02 => { self.pc },
0x03 => { self.acr },
0x04 => { self.gp1 },
0x05 => { self.gp2 },
0x06 => { self.gp3 },
0x07 => { self.gp4 },
0x08 => { self.gp5 },
0x09 => { self.gp6 },
0x0a => { self.gp7 },
0x0b => { self.gp8 },
_ => { panic!("Register not found {}", reg) }
}
}
fn setr(&mut self, reg: Instr, val: i32) {
match reg {
0x01 => { self.ci = val as u8 },
0x02 => { self.pc = val },
0x03 => { self.acr = val },
0x04 => { self.gp1 = val },
0x05 => { self.gp2 = val },
0x06 => { self.gp3 = val },
0x07 => { self.gp4 = val },
0x08 => { self.gp5 = val },
0x09 => { self.gp6 = val },
0x0a => { self.gp7 = val },
0x0b => { self.gp8 = val },
_ => { panic!("Register not found {}", reg) }
}
}
fn dbg(&mut self, regs: bool, mem: bool) {
if regs {
println!("ci : {}", self.ci );
println!("pc : {}", self.pc );
println!("acr: {}", self.acr);
println!("gp1: {}", self.gp1);
println!("gp2: {}", self.gp2);
println!("gp3: {}", self.gp3);
println!("gp4: {}", self.gp4);
println!("gp5: {}", self.gp5);
println!("gp6: {}", self.gp6);
println!("gp7: {}", self.gp7);
println!("gp8: {}\n", self.gp8);
}
}
fn exec(&mut self) -> Signals {
match self.ci {
// 01: 2 args. movir imm, reg
0x01 => {
let imm1 = self.fetcha();
let imm2 = self.fetcha();
let imm = ((imm1 as i32) << 8) + imm2 as i32;
let reg = self.fetcha();
self.setr(reg, imm);
Signals::Success
}
// 02: 2 args. movrr reg, reg
0x02 => {
let regf = self.fetcha();
let regt = self.fetcha();
let regv = self.getr(regf);
self.setr(regt, regv);
Signals::Success
}
// 03: 2 args. movrm reg, mem
// 04: 2 args. movim imm, mem
// 05: 2 args. addrr reg, reg
0x05 => {
let rego = self.fetcha();
let regt = self.fetcha();
let regov = self.getr(rego);
let regtv = self.getr(regt);
self.acr = regov + regtv;
Signals::Success
}
// 06: 2 args. addir imm, reg
// ff: 0 args. halt
0xff => {
Signals::Halt
}
_ => {
Signals::ErrNoInstruction
}
}
}
fn load(&mut self, prog: Program, loc: usize) {
let mut ld = 0;
for i in loc..prog.len() {
self.mem.mem[i] = prog[ld];
println!("{:2x} -> {:12x}", prog[ld], i);
ld += 1;
}
}
fn run(&mut self, dbgR: bool) {
let mut sig: Signals = Signals::Success;
self.dbg(dbgR, false);
while sig == Signals::Success {
self.fetchi();
sig = self.exec();
self.dbg(dbgR, false);
}
}
}
struct ArcateBus {
data: Instr,
addr: i32,
todo: Signals,
mem : ArcateMem,
}
impl ArcateBus {
fn exec(&mut self) -> Instr {
if self.todo == Signals::BusWrite {
self.mem.mem[self.addr as usize] = self.data;
0x00
} else if self.todo == Signals::BusRead {
self.mem.mem[self.addr as usize]
}else {
0xFF
}
}
}
#[derive(Copy, Clone)]
struct ArcateMem {
mem: [Instr; 0xFFFF*0xFFFF],
}
impl ArcateMem {
fn new() -> Self {
ArcateMem {
mem: [0; 0xFFFF*0xFFFF],
}
}
}
fn main() {
let mem: ArcateMem = ArcateMem::new();
let mut arc: Arcate = Arcate::new(mem);
let prog = vec![
0x01, 0xfe, 0xfe, 0x04,
0x01, 0x01, 0x01, 0x05,
0x05, 0x04, 0x05,
0xff,
];
arc.load(prog, 0x0000);
arc.run(true);
}
I think the issue is around ArcateMem, you are allocating 0xFFFF*0xFFFF Instr, which is about 4GB. With the way the code is written right now, you are allocating this on the stack, which generally can't support allocations this large. You'll probably want to use Box<> to allocate your memory in the heap, as it is more likely able to deal with allocations this large.
It's possible you could configure your operating system to increase your stack size, but I'd recommend using the heap.

Procedural macro for retrieving data from a nested struct by index

I am trying to write a rust derive macro for retrieving data from a nested struct by index. The struct only contains primitive types u8, i8, u16, i16, u32, i32, u64, i64, or other structs thereof. I have an Enum which encapsulates the leaf field data in a common type which I call an Item(). I want the macro to create a .get() implementation which returns an item based on a u16 index.
Here is the desired behavior.
#[derive(Debug, PartialEq, PartialOrd, Copy, Clone)]
pub enum Item {
U8(u8),
I8(i8),
U16(u16),
I16(i16),
U32(u32),
I32(i32),
U64(u64),
I64(i64),
}
struct NestedData {
a: u16,
b: i32,
}
#[derive(GetItem)]
struct Data {
a: i32,
b: u64,
c: NestedData,
}
let data = Data {
a: 42,
b: 1000,
c: NestedData { a: 500, b: -2 },
};
assert_eq!(data.get(0).unwrap(), Item::I32(42));
assert_eq!(data.get(1).unwrap(), Item::U64(1000));
assert_eq!(data.get(2).unwrap(), Item::U16(500));
assert_eq!(data.get(3).unwrap(), Item::I32(-2));
For this particular example, I want the macro to expand to the following...
impl Data {
pub fn get(&self, index: u16) -> Result<Item, Error> {
match index {
0 => Ok(Item::U16(self.a)),
1 => Ok(Item::I32(self.b)),
2 => Ok(Item::I32(self.c.a)),
3 => Ok(Item::U64(self.c.b)),
_ => Err(Error::BadIndex),
}
}
}
I have a working macro for a single layer struct, but I am not sure about how to modify it to support nested structs. Here is where I am at...
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Data, DataStruct, DeriveInput, Fields, Type, TypePath};
pub fn impl_get_item(input: DeriveInput) -> syn::Result<TokenStream> {
let model_name = input.ident;
let fields = match input.data {
Data::Struct(DataStruct {
fields: Fields::Named(fields),
..
}) => fields.named,
_ => panic!("The GetItem derive can only be applied to structs"),
};
let mut matches = TokenStream::new();
let mut item_index: u16 = 0;
for field in fields {
let item_name = field.ident;
let item_type = field.ty;
let ts = match item_type {
Type::Path(TypePath { path, .. }) if path.is_ident("u8") => {
quote! {#item_index => Ok(Item::U8(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("i8") => {
quote! {#item_index => Ok(Item::I8(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("u16") => {
quote! {#item_index => Ok(Item::U16(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("i16") => {
quote! {#item_index => Ok(Item::I16(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("u32") => {
quote! {#item_index => Ok(Item::U32(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("i32") => {
quote! {#item_index => Ok(Item::I32(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("u64") => {
quote! {#item_index => Ok(Item::U64(self.#item_name)),}
}
Type::Path(TypePath { path, .. }) if path.is_ident("i64") => {
quote! {#item_index => Ok(Item::I64(self.#item_name)),}
}
_ => panic!("{:?} uses unsupported type {:?}", item_name, item_type),
};
matches.extend(ts);
item_index += 1;
}
let output = quote! {
#[automatically_derived]
impl #model_name {
pub fn get(&self, index: u16) -> Result<Item, Error> {
match index {
#matches
_ => Err(Error::BadIndex),
}
}
}
};
Ok(output)
}
I'm not going to give a complete answer as my proc-macro skills are non-existant, but I don't think the macro part is tricky once you've got the structure right.
The way I'd approach this is to define a trait that all the types will use. I'm going to call this Indexible which is probably bad. The point of the trait is to provide the get function and a count of all fields contained within this object.
trait Indexible {
fn nfields(&self) -> usize;
fn get(&self, idx:usize) -> Result<Item>;
}
I'm using fn nfields(&self) -> usize rather than fn nfields() -> usize as taking &self means I can use this on vectors and slices and probably some other types (It also makes the following code slightly neater).
Next you need to implement this trait for your base types:
impl Indexible for u8 {
fn nfields(&self) -> usize { 1 }
fn get(&self, idx:usize) -> Result<Item> { Ok(Item::U8(*self)) }
}
...
Generating all these is probably a good use for a macro (but the proc macro that you're talking about).
Next, you need to generate these for your desired types: My implementations look like this:
impl Indexible for NestedData {
fn nfields(&self) -> usize {
self.a.nfields() +
self.b.nfields()
}
fn get(&self, idx:usize) -> Result<Item> {
let idx = idx;
// member a
if idx < self.a.nfields() {
return self.a.get(idx)
}
let idx = idx - self.a.nfields();
// member b
if idx < self.b.nfields() {
return self.b.get(idx)
}
Err(())
}
}
impl Indexible for Data {
fn nfields(&self) -> usize {
self.a.nfields() +
self.b.nfields() +
self.c.nfields()
}
fn get(&self, idx:usize) -> Result<Item> {
let idx = idx;
if idx < self.a.nfields() {
return self.a.get(idx)
}
let idx = idx - self.a.nfields();
if idx < self.b.nfields() {
return self.b.get(idx)
}
let idx = idx - self.b.nfields();
if idx < self.c.nfields() {
return self.c.get(idx)
}
Err(())
}
}
You can see a complete running version in the playground.
These look like they can be easily generated by a macro.
If you want slightly better error messages on types that wont work, you should explicitly trea each member as an Indexible like this: (self.a as Indexible).get(..).
It might seem that this is not going to be particularly efficient, but the compiler is able to determine that most of these pieces are constant and inline them. For example, using rust 1.51 with -C opt-level=3, the following function
pub fn sum(data: &Data) -> usize {
let mut sum = 0;
for i in 0..data.nfields() {
sum += match data.get(i) {
Err(_) => panic!(),
Ok(Item::U8(v)) => v as usize,
Ok(Item::U16(v)) => v as usize,
Ok(Item::I32(v)) => v as usize,
Ok(Item::U64(v)) => v as usize,
_ => panic!(),
}
}
sum
}
compiles to just this
example::sum:
movsxd rax, dword ptr [rdi + 8]
movsxd rcx, dword ptr [rdi + 12]
movzx edx, word ptr [rdi + 16]
add rax, qword ptr [rdi]
add rax, rdx
add rax, rcx
ret
You can see this in the compiler explorer

serde: Stateful deserialisation at top level

I'm trying to deserialise a binary format (OpenType) which consists of data in multiple tables (binary structs). I would like to be able to deserialise the tables independently (because of how they're stored in the top-level file structure; imagine them being in separate files, so they have to be deserialised separately), but sometimes there are dependencies between them.
A simple example is the loca table which contains an array of either 16-bit or 32-bit offsets, depending on the value of the indexToLocFormat field in the head table. As a more complex example, these loca table offsets in turn are used as offsets into the binary data of the glyf table to locate elements. So I need to get indexToLocFormat and loca: Vec<32> "into" the serializer somehow.
Obviously I need to implement Deserialize myself and write visitors, and I've got my head around doing that. When there are dependencies from a table to a subtable, I've also been able to work that out using deserialize_seed inside the table's visitor. But I don't know how to apply that to pass in information between tables.
I think I need to store what is essentially configuration information (value of indexToLocFormat, array of offsets) when constructing my serializer object:
pub struct Deserializer<'de> {
input: &'de [u8],
ptr: usize,
locaShortVersion: Option<bool>,
glyfOffsets: Option<Vec<u32>>,
...
}
The problem is that I don't know how to retrieve that information when I'm inside the Visitor impl for the struct; I don't know how to get at the deserializer object at all, let alone how to type things so that I get at my Deserializer object with the configuration fields, not just a generic serde::de::Deserializer:
impl<'de> Visitor<'de> for LocaVisitor {
type Value = Vec<u32>;
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(formatter, "A loca table")
}
fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
let locaShortVersion = /* what goes here? */;
if locaShortVersion {
Ok(seq.next_element::Vec<u16>()?
.ok_or_else(|| serde::de::Error::custom("Oops"))?
.map { |x| x as u32 }
} else {
Ok(seq.next_element::Vec<u32>()?
.ok_or_else(|| serde::de::Error::custom("Oops"))?
}
}
}
(terrible code here; if you're wondering why I'm writing Yet Another OpenType Parsing Crate, it's because I want to both read and write font files.)
Actually, I think I've got it. The trick is to do the deserialization in stages. Rather than calling the deserializer module's from_bytes function (which wraps the struct creation, and T::deserialize call), do this instead:
use serde::de::DeserializeSeed; // Having this trait in scope is also key
let mut de = Deserializer::from_bytes(&binary_loca_table);
let ssd: SomeSpecialistDeserializer { ... configuration goes here .. };
let loca_table: Vec<u32> = ssd.deserialize(&mut de).unwrap();
In this case, I use a LocaDeserializer defined like so:
pub struct LocaDeserializer { locaIs32Bit: bool }
impl<'de> DeserializeSeed<'de> for LocaDeserializer {
type Value = Vec<u32>;
fn deserialize<D>(self, deserializer: D) -> std::result::Result<Self::Value, D::Error>
where
D: serde::de::Deserializer<'de>,
{
struct LocaDeserializerVisitor {
locaIs32Bit: bool,
}
impl<'de> Visitor<'de> for LocaDeserializerVisitor {
type Value = Vec<u32>;
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
write!(formatter, "a loca table")
}
fn visit_seq<A>(self, mut seq: A) -> std::result::Result<Vec<u32>, A::Error>
where
A: SeqAccess<'de>,
{
if self.locaIs32Bit {
Ok(seq.next_element::<u32>()?.ok_or_else(|| serde::de::Error::custom(format!("Expecting a 32 bit glyph offset")))?)
} else {
Ok(seq.next_element::<u16>()?.ok_or_else(|| serde::de::Error::custom(format!("Expecting a 16 bit glyph offset")))?
.iter()
.map(|x| (*x as u32) * 2)
.collect())
}
}
}
deserializer.deserialize_seq(LocaDeserializerVisitor {
locaIs32Bit: self.locaIs32Bit,
})
}
}
And now:
fn loca_de() {
let binary_loca = vec![
0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a,
];
let mut de = Deserializer::from_bytes(&binary_loca);
let cs: loca::LocaDeserializer = loca::LocaDeserializer { locaIs32Bit: false };
let floca: Vec<u32> = cs.deserialize(&mut de).unwrap();
println!("{:?}", floca);
// [2, 0, 2, 0, 0, 52]
let mut de = Deserializer::from_bytes(&binary_loca);
let cs: loca::LocaDeserializer = loca::LocaDeserializer { locaIs32Bit: true };
let floca: Vec<u32> = cs.deserialize(&mut de).unwrap();
println!("{:?}", floca);
// [65536, 65536, 26]
}
Serde is very nice - once you have got your head around it.

Do I necessarily need to Box here?

Is there any way to make this code work without using Boxing:
fn some_func(my_type: MyType, some_str: &str) -> bool {
let mut hmac = match my_type {
MyType::MyType1 => create_hmac(Sha256::new(), some_str),
MyType::MyType2 => create_hmac(Sha384::new(), some_str),
MyType::MyType3 => create_hmac(Sha512::new(), some_str),
_ => panic!()
};
//some calculations goes HERE, NOT in create_hmac function...
hmac.input("fdsfdsfdsfd".to_string().as_bytes());
//something else....
true
}
fn create_hmac<D: Digest>(digest: D, some_str: &str) -> Hmac<D> {
Hmac::new(digest, some_str.to_string().as_bytes())
}
The library it's using is https://github.com/DaGenix/rust-crypto
You need to either Box or use a reference, as a "trait object" can only work behind a pointer.
Here's a very simplified version of your code. You have three different structs that implement the same trait (Digest)
struct Sha256;
struct Sha384;
struct Sha512;
trait Digest {}
impl Digest for Sha256 {}
impl Digest for Sha384 {}
impl Digest for Sha512 {}
struct HMac<D: Digest> { d: D }
fn main() {
let a = 1;
// what you're trying to do
// (does not work, Sha256, Sha384 and Sha512 are different types)
//let _ = match a {
// 1 => Sha256,
// 2 => Sha384,
// 3 => Sha512,
// _ => unreachable!()
//};
}
Note that in the real case, not only all ShaXXX types are different for the type system, they have a different memory layout as well (compare Engine256State with Engine512State for instance), so this rules out unsafe tricks with transmute.
So, as said, you can use Box or references (but you have to pre-create a concrete instance before the match if you want to use references):
fn main() {
let a = 1;
let _ : Box<Digest> = match a {
1 => Box::new(Sha256),
2 => Box::new(Sha384),
3 => Box::new(Sha512),
_ => unreachable!()
};
// to use references we need a pre-existing instance of all ShaXXX
let (sha256, sha384, sha512) = (Sha256, Sha384, Sha512);
let _ : &Digest = match a {
1 => &sha256, //... otherwise the reference wouldn't outlive the match
2 => &sha384,
3 => &sha512,
_ => unreachable!()
};
}
Note that a Box is doing the equivalent of what most Garbage Collected languages do for you under the hood when you want to only use an object through its interface. Some memory is dynamically allocated for the concrete objects, but you're only really allowed to pass around a pointer to the memory.
In your case (but I haven't tested the code below) you should be able to do:
//HMac implements a Mac trait, so we can return a Box<Mac>
// (I'm assuming you only want to use HMac through its Mac trait)
fn create_hmac<'a, D: Digest>(digest: D, some_str: &'a str) -> Box<Mac + 'a> {
Box::new(Hmac::new(digest, some_str.to_string().as_bytes()))
}
and use it as:
let mut hmac: Box<Mac> = match my_type {
MyType::MyType1 => create_hmac(Sha256::new(), some_str),
MyType::MyType2 => create_hmac(Sha384::new(), some_str),
MyType::MyType3 => create_hmac(Sha512::new(), some_str),
_ => unreachable!()
};
One addition and one clarification to Paolo's good answer. First, you could make your enum incorporate the appropriate Sha* struct and then implement Digest by delegating it as appropriate. This might not make sense in all cases, but if conceptually that's what you are doing this might make sense:
struct Sha256;
struct Sha384;
struct Sha512;
trait Digest { fn digest(&self); }
impl Digest for Sha256 { fn digest(&self) {println!("256")} }
impl Digest for Sha384 { fn digest(&self) {println!("384")} }
impl Digest for Sha512 { fn digest(&self) {println!("512")} }
enum MyType {
One(Sha256),
Two(Sha384),
Three(Sha512),
}
impl Digest for MyType {
fn digest(&self) {
use MyType::*;
match *self {
One(ref sha) => sha.digest(),
Two(ref sha) => sha.digest(),
Three(ref sha) => sha.digest(),
}
}
}
fn main() {
let a = MyType::Two(Sha384);
a.digest()
}
Also, you don't have to actually instantiate all of the types if you want to use references, you just have to ensure that the one you use is available. You also have to have places where the reference can live beyond the match expression:
#![feature(std_misc)]
#![feature(io)]
use std::time::duration::Duration;
use std::old_io::timer::sleep;
struct Sha256(u8);
struct Sha384(u8);
struct Sha512(u8);
impl Sha256 { fn new() -> Sha256 { sleep(Duration::seconds(1)); Sha256(1) }}
impl Sha384 { fn new() -> Sha384 { sleep(Duration::seconds(2)); Sha384(2) }}
impl Sha512 { fn new() -> Sha512 { sleep(Duration::seconds(3)); Sha512(3) }}
trait Digest {}
impl Digest for Sha256 {}
impl Digest for Sha384 {}
impl Digest for Sha512 {}
fn main() {
let a = 1;
let sha256: Sha256;
let sha384: Sha384;
let sha512: Sha512;
let _ : &Digest = match a {
1 => {
sha256 = Sha256::new();
&sha256
},
2 => {
sha384 = Sha384::new();
&sha384
},
3 => {
sha512 = Sha512::new();
&sha512
},
_ => unreachable!()
};
}

Resources