How do you join multiple arrays of different length together? - rust

I need to join multiple arrays of u8's that are of different sizes together to create a block header for blockchain related stuff, I tried the concat method but that only works if all the arrays are of the same length.
pub struct Block{
pub prev_block_hash: [u8;32],
pub timestamp : [u8;8],
pub difficuly : [u8;4],
pub block_height : [u8;4],
pub nonce: [u8;4],
pub version : [u8;4]
}
pub fn create_header(&self) -> [u8 ; 56]{
let buffer :[ u8 ; 56] = [0;56];
let buffer_as_vec = [self.version, self.prev_block_hash,self.timestamp,self.difficuly,self.block_height,self.nonce].concat();
buffer_as_vec.try_into().expect("Wrong size")
}

You can turn the arrays into slices:
let buffer_as_vec = [
self.version.as_slice(),
self.prev_block_hash.as_slice(),
self.timestamp.as_slice(),
self.difficuly.as_slice(),
self.block_height.as_slice(),
self.nonce.as_slice(),
]
.concat();
Or
let buffer_as_vec = [
&self.version[..],
&self.prev_block_hash[..],
&self.timestamp[..],
&self.difficuly[..],
&self.block_height[..],
&self.nonce[..],
]
.concat();

Related

Prehash a struct

I have struct with many fields that I want to use as a key in a HashMap.
I often need to use the struct multiple times to access different HashMaps and I don't want to compute the hash and possible clone each time as the program needs to be as performant as possible and it will be accessing the HashMaps a lot (billions of times so the time really stacks up).
Here is a simplified example:
use std::collections::HashMap;
#[derive(Hash, Eq, PartialEq, Clone)]
struct KeyStruct {
field1: usize,
field2: bool,
}
fn main() {
// This is what I'm doing now
let key = KeyStruct { field1: 1, field2: true };
// This is what I'd like to do
// let key = key.get_hash()
let mut map1 = HashMap::new();
let mut map2 = HashMap::new();
let mut map3 = HashMap::new();
let mut map4 = HashMap::new();
if !map1.contains_key(&key) {
map1.insert(key.clone(), 1);
}
if !map2.contains_key(&key) {
map2.insert(key.clone(), 2);
}
if !map3.contains_key(&key) {
map3.insert(key.clone(), 3);
}
if !map4.contains_key(&key) {
map4.insert(key.clone(), 4);
}
}
I never actually use the values in the KeyStruct, I just want to use it as a key to the HashMaps. I would like to avoid hashing it multiple times and cloning it like is done in that example.

Guaranteeing struct layout for wasm in vector of structs

When using a vector of #[wasm_bindgen] structs in javascript, is there a guarantee that the order of the struct's fields will be maintained so that bytes in wasm memory can be correctly interpreted in JS? I would like to be able to have a vector of structs in Rust and deterministically be able to reconstruct the structs on the JS side without having to serialize across the wasm boundary with serde. I have the following Rust code:
#[wasm_bindgen]
pub struct S {
a: u8,
b: u16,
}
#[wasm_bindgen]
pub struct Container {
ss: Vec<S>,
}
#[wasm_bindgen]
impl Container {
pub fn new() -> Self {
let ss = (0..10_u8)
.map(|i| S {
a: i,
b: (2 * i).into(),
})
.collect();
Self { ss }
}
pub fn items_ptr(&self) -> *const S {
self.ss.as_ptr()
}
pub fn item_size(&self) -> usize {
std::mem::size_of::<S>()
}
pub fn buffer_len(&self) -> usize {
self.ss.len() * self.item_size()
}
}
Now, on the JS side, I have the following:
import { memory } from "rust_wasm/rust_wasm.wasm";
import * as wasm from "rust_wasm";
const container = wasm.Container.new();
const items = new Uint8Array(memory.buffer, container.items_ptr(), container.buffer_len());
function getItemBytes(n) {
const itemSize = container.item_size();
const start = n * itemSize;
const end = start + itemSize;
return items.slice(start, end);
}
With the above code, I can obtain the (four, due to alignment) bytes that comprise an S in JS. Now, my question: how do I interpret those bytes to reconstruct the S (the fields a and b) in JS? In theory, I'd like to be able to say something like the following:
const itemBytes = getItemBytes(3);
const a = itemBytes[0];
const b = itemBytes[1] << 8 + itemBytes[2]
But of course this relies on the fact that the layout in memory of an instance of S matches its definition, that u16 is big endian, etc. Is it possible to get Rust to enforce these properties so that I can safely interpret bytes in JS?

Rust hit an API that returns an array of arrays

I am new to rust and working on a tool which interacts with the Binance API. The API returns a response like so:
{
"lastUpdateId": 1027024,
"bids": [
[
"4.00000000", // PRICE
"431.00000000" // QTY
]
],
"asks": [
[
"4.00000200",
"12.00000000"
]
]
}
Bids and Asks are an array of arrays. I have to declare the struct for the API response.
I currently have
use reqwest::Error;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Depth {
lastUpdateId: u32,
bids: Vec<u64>,
asks: Vec<u64>,
}
#[tokio::main]
pub async fn order_book() -> Result<(), Error> {
let request_url = format!("https://api.binance.us/api/v3/depth?symbol=BTCUSD");
println!("{}", request_url);
let response = reqwest::get(&request_url).await?;
println!("Status: {}", response.status());
let depth: Depth = response.json().await?;
println!("{:?}", depth);
Ok(())
}
I believe I am declaring the type for bids and asks incorrectly but I am unable to determine how to declare an array of arrays. I get a response.status of 200, but I am unable to print out the response.json.
Thank You!
Since it returns an array of arrays, you need to nest the types. bids: Vec<Vec<u64>> should work, but since you know each inner array will have two elements, you can make it more efficient with bids: Vec<[u64; 2]> - a dynamically-sized vector of fixed-size arrays, or bids: Vec<(u64, u64)>, a dynamically-sized vector of tuples. You'd also need to update asks similarly.
Your final code could look something like this:
use reqwest::Error;
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Depth {
lastUpdateId: u32,
bids: Vec<(u64, u64)>,
asks: Vec<(u64, u64)>,
}
#[tokio::main]
pub async fn order_book() -> Result<(), Error> {
let request_url = format!("https://api.binance.us/api/v3/depth?symbol=BTCUSD");
println!("{}", request_url);
let response = reqwest::get(&request_url).await?;
println!("Status: {}", response.status());
let depth: Depth = response.json().await?;
println!("{:?}", depth);
Ok(())
}

Adding a new key to each element of vector in Rust

I have a vector of structures. I want to add one additional field to each element. What's the best way to do that?
Something like this:
// Pseudo code
let items = vec![elem1, elem2, elem3, elem4];
for x in items {
// Something like this
x["some_additional_key"] = get_data(x);
}
//
// Now I have items[i].some_additional_key in each element
Rust is a statically-typed language; you may be familiar with other similar languages like C++, Java or Swift. In these languages, the members, types, and layout of a struct are fixed when the program is compiled.
Because of this, there's no way to add a new struct field at runtime — no "ifs", "ands", or "buts" — you can't do it.
Instead, you have to model that dynamic nature some other way:
Use a type that allows for arbitrary expansion. HashMap and BTreeMap (and many other similar types) allow you to have an arbitrary number of key-value pairs. Under the hood, this is basically how many dynamic languages work - a mapping of strings to arbitrary values:
use std::collections::HashMap;
#[derive(Debug, Default)]
struct Element(HashMap<String, u8>);
fn get_data(_: &Element) -> u8 {
42
}
fn main() {
let mut items = vec![
Element::default(),
Element::default(),
Element::default(),
Element::default(),
];
for x in &mut items {
let value = get_data(x);
x.0
.entry("some_additional_key".to_string())
.or_insert(value);
}
}
Use a type that allows for specific expansion. Option allows for a value to be present or not:
#[derive(Debug, Default)]
struct Element {
some_additional_key: Option<u8>,
}
fn get_data(_: &Element) -> u8 {
42
}
fn main() {
let mut items = vec![
Element::default(),
Element::default(),
Element::default(),
Element::default(),
];
for x in &mut items {
let value = get_data(x);
x.some_additional_key = Some(value);
}
}
Use composition. Create a new type that wraps your existing type:
#[derive(Debug, Default)]
struct Element;
#[derive(Debug)]
struct EnhancedElement {
element: Element,
some_additional_key: u8,
}
fn get_data(_: &Element) -> u8 {
42
}
fn main() {
let items = vec![
Element::default(),
Element::default(),
Element::default(),
Element::default(),
];
let enhanced: Vec<_> = items
.into_iter()
.map(|element| {
let some_additional_key = get_data(&element);
EnhancedElement {
element,
some_additional_key,
}
})
.collect();
}
See also:
How to lookup from and insert into a HashMap efficiently?
Update value in mutable HashMap

Implementing the Index operator for matrices with multiple parameters

I'm trying to make a Matrix struct and I want to override the Index operator to let me have matrix-style indexing.
For example:
let m = Matrix { ... DATA ... }
let entry = m[0,0]
My structure looks like this:
struct Matrix {
cols: usize,
rows: usize,
data: Vec<f32>
}
I've been looking at the Index trait and I don't see how I can make this work? Additionally I'd like to be able to take ranges in each dimension etc.
In short, you cannot do this. The Index trait is defined as:
pub trait Index<Idx: ?Sized> {
type Output: ?Sized;
fn index(&self, index: Idx) -> &Self::Output;
}
That is, it takes a single argument of type Idx. The closest you can do is to use a tuple, which is a single type with multiple values packed into it:
impl std::ops::Index<(usize, usize)> for Matrix {
type Output = f32;
fn index(&self, idx: (usize, usize)) -> &f32 {
// or as appropriate for row- or column-major data
&self.data[idx.0 * self.cols + idx.1]
}
}
And it would be called like
matrix[(0, 1)]
bluss points out that the multiarray crate uses two-element arrays instead of a tuple. This is probably easier to type as you can just hit the square brackets twice:
impl std::ops::Index<[usize; 2]> for Matrix {
type Output = f32;
fn index(&self, idx: [usize; 2]) -> &f32 {
// or as appropriate for row- or column-major data
&self.data[idx[0] * self.cols + idx[1]]
}
}
And it's called like matrix[[0, 1]]. The important thing is that there's still just a single value provided as the argument to index.
Repeat the implementation as desired for Range, RangeTo, RangeFrom, and RangeFull. These are all single types, so you can call it like matrix[5..], for whatever that might mean.
Another possibility would be to use 2D-array style indexing like m[0][1]. This is definitely possible -- even quite easy in your case. Your Index implementation just has to return something that is indexable again. Code:
use std::ops::Index;
struct Matrix {
cols: usize,
rows: usize,
data: Vec<f32>
}
impl Index<usize> for Matrix {
type Output = [f32];
fn index(&self, index: usize) -> &Self::Output {
&self.data[index * self.cols .. (index+1) * self.cols]
}
}
fn main() {
let m = Matrix {
cols: 2,
rows: 2,
data: vec![1., 2., 3., 4.],
};
println!("{} {}", m[0][0], m[0][1]);
println!("{} {}", m[1][0], m[1][1]);
}
This style is more common among languages like Java and C.

Resources