简体   繁体   中英

Templating input and output type in rust with bitvec

Here I have two functions in rust which convert a bitvec into an integer. They are identical except for their types, so I'd like to template over both:

  1. the input types BitVec and BitSlice
  2. the return type u32 or u64
use bitvec::prelude::*; // bitvec = "0.22.3"

fn bslice_to_int(bv: &BitSlice::<Lsb0, u8>) -> u32 {
  let mut int = 0;
  for bit in bv {
    int = int << 1;
    if bit == true {
      int += 1;
    }
  }
  return int;
}

fn bv_to_int(bv: &BitVec::<Lsb0, u8>) -> u64 {
  let mut int = 0;
  for bit in bv {
    int = int << 1;
    if bit == true {
      int += 1;
    }
  }
  return int;
}

fn main() {
  let bits = bitvec![Lsb0, u8; 1,0,1,0];
  let val1 = bslice_to_int(&bits[0..2]);
  let val2 = bv_to_int(&bits);
  println!("{} {}", val1, val2); // "10 2"
}

I seem to have a different problem with each.

Trying 1) to make the input type generic:

fn bv_to_int_generic_in<T>(bv: &T) -> u64 {
  let mut int = 0;
  for bit in bv {
    int = int << 1;
    if bit == true {
      int += 1;
    }
  }
  return int;
}

fn main() {
  let bits = bitvec![Lsb0, u8; 1,0,1,0];
  let val1 = bv_to_int_generic_in::<BitSlice::<Lsb0, u8>>(&bits[0..2]);
  let val2 = bv_to_int_generic_in::<BitVec::<Lsb0, u8>>(&bits);
  println!("{} {}", val1, val2);
}

doesn't compile, and gives an error that the generic T doesn't have the Iterator trait implemented:

error[E0277]: `&T` is not an iterator
   --> src/main.rs:28:14
    |
28  |   for bit in bv {
    |              ^^ `&T` is not an iterator
    |
    = help: the trait `Iterator` is not implemented for `&T`
    = note: required because of the requirements on the impl of `IntoIterator` for `&T`
note: required by `into_iter`
   --> /Users/jlees/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/iter/traits/collect.rs:234:5
    |
234 |     fn into_iter(self) -> Self::IntoIter;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Trying 2) to make the return type generic:

// Trying to template the output type
fn bv_to_int_generic_out<T>(bv: &BitVec::<Lsb0, u8>) -> T {
  let mut int: T = 0;
  for bit in bv {
    int = int << 1;
    if bit == true {
      int += 1;
    }
  }
  return int;
}

fn main() {
  let bits = bitvec![Lsb0, u8; 1,0,1,0];
  let val1: u32 = bv_to_int_generic_out::<u32>(&bits);
  let val2: u64 = bv_to_int_generic_out::<u64>(&bits);
  println!("{} {}", val1, val2);
}

also doesn't compile, and gives errors that T is not an {integer} :

error[E0308]: mismatched types
  --> src/main.rs:55:20
   |
54 | fn bv_to_int_generic_out<T>(bv: &BitVec::<Lsb0, u8>) -> T {
   |                          - this type parameter
55 |   let mut int: T = 0;
   |                -   ^ expected type parameter `T`, found integer
   |                |
   |                expected due to this
   |
   = note: expected type parameter `T`
                        found type `{integer}`

error[E0369]: no implementation for `T << {integer}`
  --> src/main.rs:57:15
   |
57 |     int = int << 1;
   |           --- ^^ - {integer}
   |           |
   |           T
   |
help: consider restricting type parameter `T`
   |
54 | fn bv_to_int_generic_out<T: std::ops::Shl<Output = {integer}>>(bv: &BitVec::<Lsb0, u8>) -> T {
   |                           +++++++++++++++++++++++++++++++++++

error[E0368]: binary assignment operation `+=` cannot be applied to type `T`
  --> src/main.rs:59:7
   |
59 |       int += 1;
   |       ---^^^^^
   |       |
   |       cannot use `+=` on type `T`
   |
help: consider restricting type parameter `T`
   |
54 | fn bv_to_int_generic_out<T: std::ops::AddAssign>(bv: &BitVec::<Lsb0, u8>) -> T {
   |                           +++++++++++++++++++++

See also this gist for the above code in one place: https://gist.github.com/johnlees/49e6a7bd85b3545bba20e8670180f24a

I am new to rust, and I realise that I'm trying to write these like C++ templates which may well be the wrong approach. Any advice on the correct rust approach for making either the input or output types generic would be appreciated!

You can create a simple macro to declare them:

use bitvec::prelude::*; // bitvec = "0.22.3"

macro_rules! bitvec_to {
    ($func_name:ident, $input_type:ty, $output_type:ty) => {
        fn $func_name(bv: &$input_type) ->  $output_type {
          let mut int = 0;
          for bit in bv {
            int = int << 1;
            if bit == true {
              int += 1;
            }
          }
          return int;
        }
    };
}

bitvec_to!(bslice_to_int, BitSlice::<Lsb0, u8>, u32);
bitvec_to!(bv_to_int, BitVec::<Lsb0, u8>, u64);

Playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM