There are a couple of ways you could go about doing this. I don't know anything about modbus, so I'm assuming that the input looks like your RAW_INPUT
above. First, you can use as
to cast the u16 to u8. This will silently truncate values larger than 255. The other, safer approach is to use std::convert::TryFrom:
Simple and safe type conversions that may fail in a controlled way under some circumstances. It is the reciprocal of TryInto
.
This is useful when you are doing a type conversion that may trivially succeed but may also need special handling. For example, there is no way to convert an i64
into an i32
using the From
trait, because an i64
may contain a value that an i32
cannot represent and so the conversion would lose data. This might be handled by truncating the i64
to an i32
(essentially giving the i64
's value modulo i32::MAX
) or by simply returning i32::MAX
, or by some other method. The From
trait is intended for perfect conversions, so the TryFrom
trait informs the programmer when a type conversion could go bad and lets them decide how to handle it.
Some illustrative code you can play around with on the Rust Playground:
#[cfg(test)]
mod tests {
use std::convert::TryFrom;
use std::num::TryFromIntError;
use std::str;
pub const RAW_BAD_INPUT: &[u16] = &[102, 111, 111, 300];
pub const RAW_GOOD_INPUT: &[u16] = &[102, 111, 111];
/// Converts using `as`. Demonstrates truncation.
#[test]
fn test_truncating() {
let expected = vec![102, 111, 111, 44]; // Note: 44
let actual = RAW_BAD_INPUT
.iter()
.map(|val| *val as u8)
.collect::<Vec<u8>>();
assert_eq!(expected, actual);
}
/// Demonstrates conversion using `TryFrom` on input with values that
/// would be truncated
#[test]
fn test_try_from_bad() {
let actual: Vec<Result<u8, TryFromIntError>> =
RAW_BAD_INPUT.iter().map(|val| u8::try_from(*val)).collect();
assert_eq!(actual[0].unwrap(), 102u8);
assert_eq!(actual[1].unwrap(), 111u8);
assert_eq!(actual[2].unwrap(), 111u8);
assert!(actual[3].is_err());
}
/// Demonstrates conversion using `TryFrom` on input with values
/// that would not be truncated. Also parses the Vec<u8> as a UTF-8
/// encoded string
#[test]
fn test_try_from_ok() {
let intermediate: Vec<u8> = RAW_GOOD_INPUT
.iter()
.map(|val| u8::try_from(*val).unwrap())
.collect();
let actual = match str::from_utf8(&intermediate) {
Ok(s) => s,
Err(e) => panic!("Invalid UTF-8: {}", e),
};
assert_eq!("foo", actual);
}
}
Using the code in test_try_from_ok
, you should now have a String
containing the data you wish to parse with nom
.