I have a static array initialized with some constant value:
static PROG_ROM: [u8; 850] = [0x12, 0x1d, ...];
I would like to instead load at compile-time the contents of a file into it. Sounds like a job for std::include_bytes!, however, I have two problems with it:
The type of include_bytes!("foo.dat") is &[u8; 850] i.e. it is a reference. I need this to be a bonafide static array.
Even if there was an include_bytes_static! macro with type [u8;850], I would have to use it like this:
static PROG_ROM: [u8; 850] = include_bytes_static!("foo.dat");
I.e. I would have to hardcode the length of the file. Instead, I
would like to take the length from the length of the file contents.
So the ideal replacement for my code would be a macro to replace the whole definition, i.e. look something like this:
define_included_bytes!(PROG_ROM, "foo.dat")
and it would expand to
static PROG_ROM: [u8; 850] = [0x12, 0x1d, ...];
So how do I do this?
Use *include_bytes!(..) to get a [u8; _] instead of &[u8; _] (since arrays implement Copy), and use include_bytes!(..).len() (which is a const method) to specify the length of the array in the type:
static PROG_ROM: [u8; include_bytes!("foo.dat").len()] = *include_bytes!("foo.dat");
As Chayim Friedman pointed out you can easily define that proc macro yourself:
#[proc_macro]
pub fn define_included_bytes(token_stream: TokenStream) -> TokenStream {
let [ident, _comma, file] = &token_stream.into_iter().collect::<Vec<_>>()[..] else {
panic!("expected invocation: `define_included_bytes!(IDENTIFIER, \"file_name\");");
};
let file = file.to_string().trim_matches('\"').to_string();
let data: Vec<u8> = std::fs::read(&file).expect(&format!("File {:?} could not be read", file));
format!("const {ident}: [u8; {}] = {:?};", data.len(), data).parse().unwrap()
}
Obviously this is just a hacked together proof of concept and you should thouroughly check the tokens instead of just assuming they're correct.
Based on cafce25's answer, I ended up writing the following version using syn and quote:
extern crate proc_macro;
extern crate syn;
extern crate quote;
use proc_macro::TokenStream;
use syn::parse::{Parse, ParseStream, Result};
use syn::{parse_macro_input, Ident, Token, LitStr};
use quote::quote;
struct StaticInclude {
name: Ident,
filepath: String,
}
impl Parse for StaticInclude {
fn parse(input: ParseStream) -> Result<Self> {
let name: Ident = input.parse()?;
input.parse::<Token![=]>()?;
let filepath: String = input.parse::<LitStr>()?.value();
Ok(StaticInclude{ name, filepath })
}
}
#[proc_macro]
pub fn progmem_include_bytes(tokens: TokenStream) -> TokenStream {
let StaticInclude{ name, filepath } = parse_macro_input!(tokens as StaticInclude);
let data: Vec<u8> = std::fs::read(&filepath).expect(&format!("File {:?} could not be read", filepath));
let len = data.len();
TokenStream::from(quote! {
#[link_section = ".progmem.data"]
static #name: [u8; #len] = [#(#data),*];
})
}
(never mind the link_section attribute, that's an AVR-ism).
This works well, except Cargo is not tracking the dependency on the external file, so if its content changes, the program using progmem_include_bytes! is not recompiled by cargo build.
Related
I am trying to create a static array of objects that implement a common trait. All these structs and their sizes are known at compile time. But when accessing a field defined on the struct the compiler tells me that the field is not on the type.
fn main() {
for &thing in ALL_THINGS {
println!("{}", thing.name)
}
}
trait Thing: Sync { }
struct SpecificThing {
name: &'static str
}
impl Thing for SpecificThing { }
static ALL_THINGS: &'static [&dyn Thing] = &[&SpecificThing {name: "test"}];
error[E0609]: no field `name` on type `&dyn Thing`
--> src/main.rs:3:30
|
3 | println!("{}", thing.name)
| ^^^^
Example:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=28a29e98cadf97edb8d4ec61703e8959
The questions static array of trait objects, Create vector of objects implementing a trait in Rust, Can I have a static borrowed reference to a trait object? or Vector of objects belonging to a trait doesn't help with explaining why this happens or how to resolve it.
Please what am I doing wrong here? Is there a better method to solve this task that I have not found yet?
When you define &dyn Thing, you erase all the information about specific data type. That means you can't access fields of dinamically dispatched objects.
Just imagine that you have two different structs in ALL_THINGS:
struct SpecificThing {
name: &'static str
}
struct SpecificAnotherThing {
no_name: &'static str
}
static ALL_THINGS: &'static [&dyn Thing] = &[&SpecificThing {name: "test"}, &SpecificAnotherThing { no_name: "" }];
You can't access name field because trait Thing know nothing about concrete types it implemented for. Therefore you can't access it's fields directly.
If you really need it, you should define a method in Thing trait which will return value you need:
trait Thing: Sync {
fn name(&self) -> &str;
}
// ...
// ...
impl Thing for SpecificThing {
fn name(&self) -> &str {
self.name
}
}
Or you can use static dispatching and algebraic data types (enum).
You can't access SpecificThing.name from a &dyn Thing since not all Things have a name field (ignoring the fact that traits don't have fields).
Your use of dyn Thing suggests you have a set of objects (structs/enums) that have some things in common. All these commonalities must be present in Thing for you to access them. For example, if a name is a common thing, you could add a function that gets the name:
fn main() {
for &thing in ALL_THINGS {
println!("{}", thing.get_name())
}
}
trait Thing: Sync {
fn get_name(&self) -> &'static str;
}
struct SpecificThing {
name: &'static str
}
impl Thing for SpecificThing {
fn get_name(&self) -> &'static str {
self.name
}
}
static ALL_THINGS: &'static [&dyn Thing] = &[&SpecificThing {name: "test"}];
I have a function like this in my static library crate:
use super::*;
static BLANK_VEC: [u8; 16] = [0_u8; 16];
pub fn pad(name: &'static str) -> String {
let mut res = String::from(name);
res.push_str(&String::from_utf8_lossy(&BLANK_VEC[name.len()..]));
res
}
When I link this to C code it works as expected, but if I link with the below code (the only difference being const instead of static) the label BLANK_VEC doesn't appear in the ELF file. It could compile and run until it gets a HardFault.
use super::*;
const BLANK_VEC: [u8; 16] = [0_u8; 16];
pub fn pad(name: &'static str) -> String {
let mut res = String::from(name);
res.push_str(&String::from_utf8_lossy(&BLANK_VEC[name.len()..]));
res
}
Is this a bug on the Rust side? I think so because the const variable goes out of scope somehow. I can reference it and it compiles. Where is the ensured memory safety? Why didn't I have to use unsafe block to do that?
If this is something that depends on my linker: I use arm-gcc-none-eabi.
Edit: I understand why this happens but shouldn't Rust ensure that the user uses a variable that won't disappear?
It's not a bug in rust: const defines a constant value copied at every use-site (therefore not existing at runtime).
static defines a global variable (which may or may not be constant), and is thus present in the final program, it's an actual single location in memory.
I'm writing a MQTT5 library. To send a packet, I need to know the size of the payload before writing the payload. My solution for determining the size has the following constraints order by importance:
be easy to maintain
should not create copies of the data
should be fairly performant (avoid double calculations)
To determine the size I can do any of the following solutions:
do the calculations by hand, which is fairly annoying
hold a copy of the data to send in memory, which I want to avoid
Build an std::iter::ExactSizeIterator for the payload which consists of std::iter::Chains itself, which leads to ugly typings fast, if you don't create wrapper types
I decided to go with version 3.
The example below shows my try on writing a MQTT String iterator. A MQTT String consists of two bytes which are the length of the string followed by the data as utf8.
use std::iter::*;
use std::slice::Iter;
pub struct MQTTString<'a> {
chain: Chain<Iter<'a, u8>, Iter<'a, u8>>,
}
impl<'a> MQTTString<'a> {
pub fn new(s: &'a str) -> Self {
let u16_len = s.len() as u16;
let len_bytes = u16_len.to_be_bytes();
let len_iter = len_bytes.iter(); // len_bytes is borrowed here
let s_bytes = s.as_bytes();
let s_iter = s_bytes.iter();
let chain = len_iter.chain(s_iter);
MQTTString { chain }
}
}
impl<'a> Iterator for MQTTString<'a> {
type Item = &'a u8;
fn next(&mut self) -> Option<&'a u8> {
self.chain.next()
}
}
impl<'a> ExactSizeIterator for MQTTString<'a> {}
pub struct MQTTStringPait<'a> {
chain: Chain<std::slice::Iter<'a, u8>, std::slice::Iter<'a, u8>>,
}
This implementation doesn't compile because I borrow len_bytes instead of moving it, so it'd get dropped before the Chain can consume it:
error[E0515]: cannot return value referencing local variable `len_bytes`
--> src/lib.rs:19:9
|
12 | let len_iter = len_bytes.iter(); // len_bytes is borrowed here
| --------- `len_bytes` is borrowed here
...
19 | MQTTString { chain }
| ^^^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function
Is there a nice way to do this? Adding len_bytes to the MQTTString struct doesn't help. Is there a better fourth option of solving the problem?
The root problem is that iter borrows the array. In nightly Rust, you can use array::IntoIter, but it does require that you change your iterator to return u8 instead of &u8:
#![feature(array_value_iter)]
use std::array::IntoIter;
use std::iter::*;
use std::slice::Iter;
pub struct MQTTString<'a> {
chain: Chain<IntoIter<u8, 2_usize>, Copied<Iter<'a, u8>>>,
}
impl<'a> MQTTString<'a> {
pub fn new(s: &'a str) -> Self {
let u16_len = s.len() as u16;
let len_bytes = u16_len.to_be_bytes();
let len_iter = std::array::IntoIter::new(len_bytes);
let s_bytes = s.as_bytes();
let s_iter = s_bytes.iter().copied();
let chain = len_iter.chain(s_iter);
MQTTString { chain }
}
}
impl<'a> Iterator for MQTTString<'a> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
self.chain.next()
}
}
impl<'a> ExactSizeIterator for MQTTString<'a> {}
You could do the same thing in stable Rust by using a Vec, but that'd be a bit of overkill. Instead, since you know the exact size of the array, you could get the values and chain more:
use std::iter::{self, *};
use std::slice;
pub struct MQTTString<'a> {
chain: Chain<Chain<Once<u8>, Once<u8>>, Copied<slice::Iter<'a, u8>>>,
}
impl<'a> MQTTString<'a> {
pub fn new(s: &'a str) -> Self {
let u16_len = s.len() as u16;
let [a, b] = u16_len.to_be_bytes();
let s_bytes = s.as_bytes();
let s_iter = s_bytes.iter().copied();
let chain = iter::once(a).chain(iter::once(b)).chain(s_iter);
MQTTString { chain }
}
}
impl<'a> Iterator for MQTTString<'a> {
type Item = u8;
fn next(&mut self) -> Option<u8> {
self.chain.next()
}
}
impl<'a> ExactSizeIterator for MQTTString<'a> {}
See also:
How to implement Iterator and IntoIterator for a simple struct?
An iterator of &u8 is not a good idea from the point of view of pure efficiency. On a 64-bit system, &u8 takes up 64 bits, as opposed to the 8 bits that the u8 itself would take. Additionally, dealing with this data on a byte-by-byte basis will likely impede common optimizations around copying memory around.
Instead, I'd recommend creating something that can write itself to something implementing Write. One possible implementation:
use std::{
convert::TryFrom,
io::{self, Write},
};
pub struct MQTTString<'a>(&'a str);
impl MQTTString<'_> {
pub fn write_to(&self, mut w: impl Write) -> io::Result<()> {
let len = u16::try_from(self.0.len()).expect("length exceeded 16-bit");
let len = len.to_be_bytes();
w.write_all(&len)?;
w.write_all(self.0.as_bytes())?;
Ok(())
}
}
See also:
How do I convert between numeric types safely and idiomatically?
Converting number primitives (i32, f64, etc) to byte representations
I'm trying to use Rust and get autocomplete data from RediSearch with the FT.SUGGET command but it ends up retrieving a None value even though when I run FT.SUGGET directly with the command prompt it properly gives responses. This is my code which format works perfectly well with sets and gets in Redis.
FT.SUGADD works properly in this as well. Thanks for the future help!
pub fn ft_sugadd(index: String, field: String) -> redis::RedisResult<()>
{
let client = redis::Client::open("redis://127.0.0.1/")?;
let mut con = client.get_connection()?;
redis::cmd("FT.SUGADD").arg(index).arg(field).arg("1".to_string()).query(&mut con)?;
Ok(())
}
pub fn ft_sugget(index: String, field: String) -> redis::RedisResult<String>
{
let client = redis::Client::open("redis://127.0.0.1/")?;
let mut con = client.get_connection()?;
let data_value = redis::cmd("FT.SUGGET").arg(index).arg(field).arg("fuzzy".to_string()).query(&mut con)?;
Ok(data_value)
}
fn main()
{
ft_sugadd("dictionary".to_string(), "Bob".to_string()).ok();
let remember = ft_sugget("dictionary".to_string(), "Bo".to_string()).ok();
println!("Hello {:?}", remember);
}
This question has been solved - the reason it wasn't working properly is that the function ft_sugget is defined to return Strings when it should in fact be returning a Vector of Strings.
I am creating Rust bindings for a C library that defines lists of standard constant default values:
// C
typedef struct RMW_PUBLIC_TYPE rmw_qos_profile_t
{
size_t depth;
enum rmw_qos_reliability_policy_t reliability;
// ...
} rmw_qos_profile_t;
enum RMW_PUBLIC_TYPE rmw_qos_reliability_policy_t
{
RMW_QOS_POLICY_RELIABILITY_RELIABLE,
RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
// ...
};
// Global value that needs wrapping
static const rmw_qos_profile_t rmw_qos_profile_sensor_data =
{
5,
RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
// ...
};
Using Bindgen, static Rust variables are generated:
// Rust
extern "C" {
#[link_name = "\u{1}rmw_qos_profile_sensor_data"]
pub static rmw_qos_profile_sensor_data: rmw_qos_profile_t;
}
but static global variables are highly inconvenient to work with in Rust, having to encase every access in an unsafe {} block. Especially when you do not need mutability.
I already wrapped the struct and enums in Rust:
// Rust
pub enum QoSReliabilityPolicy {
Reliable = 0,
BestEffort = 1,
}
impl From<rmw_qos_reliability_policy_t> for QoSReliabilityPolicy {
fn from(raw: rmw_qos_reliability_policy_t) -> Self {
match raw {
rmw_qos_reliability_policy_t::RMW_QOS_POLICY_RELIABILITY_RELIABLE => QoSReliabilityPolicy::Reliable,
rmw_qos_reliability_policy_t::RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT => QoSReliabilityPolicy::BestEffort,
}
}
}
pub struct QoSProfile {
pub depth: usize,
pub reliability: QoSReliabilityPolicy,
// ...
}
impl From<rmw_qos_profile_t> for QoSProfile {
fn from(qos_profile: rmw_qos_profile_t) -> Self {
QoSProfile {
depth: qos_profile.depth,
reliability: qos_profile.reliability.into(),
// ...
}
}
}
impl From<rmw_qos_profile_t> for QoSProfile {
fn from(qos_profile: rmw_qos_profile_t) -> Self {
QoSProfile {
depth: qos_profile.depth,
reliability: qos_profile.reliability.into(),
// ...
}
}
}
Now, I am looking for a solution to expose the same pre-defined profiles, such as rmw_qos_profile_sensor_data, to my Rust users without having to duplicate the C values manually in Rust.
Currently I am duplicating the C code in Rust:
// Rust
// Works but unsatisfying
pub const QOS_PROFILE_SENSOR_DATA: QoSProfile = QoSProfile {
depth: 5,
reliability: QoSReliabilityPolicy::BestEffort,
// ...
};
But this is not satisfying. When the upstream C library updates these values, users will experience inconsistent behaviour and bugs.
What are the possible solutions for conveniently wrapping these global constants ?
The ideal solution would:
Automatically update the values when the upstream C library changed
Expose global consts so that these values can be inlined by the compiler
If not possible, expose global immutable variables
If still not possible, at least not require unsafe
The problem that I have been facing is that, since static const C structures are stored in memory, they can't ben translated into a const so easily and this is probably why Bindgen translates it using the static keyword.
So, the possibilities that I can imagine, but don't know how to execute, are:
Have smarter parsing of the C code to generate Rust code ?
Use some form of macro ?
Initialize from the C lib's static memory in the prelude ?
Initialize from the C lib's static memory explicitly ?
Other solutions ?