I am completely new to Rust coming from JS/TS
I have already seen other questions like: How do I iterate over elements of a struct in Rust? but they didn't get me to a real answer.
I am trying to iterate over the keys and values of a struct in rust
In JS/TS this would work like this:
const o = {
a: "hello",
b: "world"
};
const keys = Object.keys(o);
const values = Object.values(o);
// now loop over them
How would something like this work in Rust?
I am using Serde to parse a config yaml file to a struct.
#[derive(Deserialize, Debug, Clone)]
pub struct Config {
pub headers: Headers,
}
#[derive(Deserialize, Debug, Clone)]
pub struct Headers {
#[serde(rename = "Content-Security-Policy")]
pub content_security_policy: String,
#[serde(rename = "x-frame-options")]
pub x_frame_options: String,
#[serde(rename = "x-content-type-options")]
pub x_content_type_options: String,
#[serde(rename = "x-permitted-cross-domain-policies")]
pub x_permitted_cross_domain_policies: String,
#[serde(rename = "x-download-options")]
pub x_download_options: String,
#[serde(rename = "x-xss-protection")]
pub x_xss_protection: String,
#[serde(rename = "referrer-policy")]
pub referrer_policy: String,
#[serde(rename = "Strict-Transport-Security")]
pub strict_transport_security: String,
#[serde(rename = "feature-policy")]
pub feature_policy: String,
#[serde(rename = "Cache-Control")]
pub cache_control: String,
}
But this does not implement the .iter() function and i haven't found a solution searching for this.
Thanks to Caesar
I tried this:
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Debug, Clone, Serialize)]
pub struct Config {
pub headers: Headers,
}
#[derive(Deserialize, Debug, Clone, Serialize)]
pub struct Headers {
#[serde(rename = "Content-Security-Policy")]
pub content_security_policy: String,
#[serde(rename = "x-frame-options")]
pub x_frame_options: String,
#[serde(rename = "x-content-type-options")]
pub x_content_type_options: String,
#[serde(rename = "x-permitted-cross-domain-policies")]
pub x_permitted_cross_domain_policies: String,
#[serde(rename = "x-download-options")]
pub x_download_options: String,
#[serde(rename = "x-xss-protection")]
pub x_xss_protection: String,
#[serde(rename = "referrer-policy")]
pub referrer_policy: String,
#[serde(rename = "Strict-Transport-Security")]
pub strict_transport_security: String,
#[serde(rename = "feature-policy")]
pub feature_policy: String,
#[serde(rename = "Cache-Control")]
pub cache_control: String,
}
let iterable_headers: HashMap<String, String> =
serde_yaml::from_value(serde_yaml::to_value(&config.headers).unwrap()).unwrap();
for header in &iterable_headers {
res = res.header(header.0, header.1);
}
Related
I have a C library that expects string type that explicitly defines the string length:
#[repr(C)]
pub struct FFIStr {
len: usize,
data: *const u8,
}
Because this type is used as a static, I'd like a way to safely declare it using a const function or macro (instead of manually setting len).
My first attempt was to use a macro and len(), however in versions before 1.39.0, it is not possible to get the length of a slice as a const fn:
macro_rules! ffi_string {
($x:expr) => {
FFIStr { len: $x.len(), data: $x as *const u8 }
};
}
#[no_mangle]
pub static mut HELLO_WORLD: FFIStr = ffi_string!(b"Hello, world!");
error: core::slice::<impl [T]>::len` is not yet stable as a const function
My second attempt was to use std::mem::size_of<T>, but there doesn't appear to be a way to get the type of the static array short of using generics:
const fn ffi_string<T>(s: &'static T) -> FFIStr {
FFIStr { len: ::std::mem::size_of::<T>(), data: s as *const _ as *const _ }
}
#[no_mangle]
pub static mut HELLO_WORLD: FFIStr = ffi_string(b"Hello, world!");
While this works (surprisingly), it's horribly prone to misuse as it wildly casts whatever you pass it to a *const u8.
It seems like const_generics would be a nice solution to this, but they're currently unstable:
const fn ffi_string<const SIZE: usize>(s: &'static [u8; SIZE]) -> FFIStr {
FFIStr { len: SIZE, data: s as *const u8 }
}
#[no_mangle]
pub static mut X: FFIStr = ffi_string(b"Hello, world!");
error[E0658]: const generics are unstable
Is there a better way of determining the size of a static array at compile time?
In Rust 1.39.0 [T]::len was stabilised as a const function, now making this straight forward:
const ARRAY: [i32; 3] = [1, 2, 3];
const ARRAY_SIZE: usize = ARRAY.len();
fn main() {
assert_eq!(3, ARRAY_SIZE);
}
In earlier versions of Rust, here's one way based on the common C ARRAY_SIZE macro:
macro_rules! array_size {
($x:expr) => (
(size_of_val($x) / size_of_val(&$x[0]))
)
}
const fn size_of_val<T>(_: &T) -> usize {
std::mem::size_of::<T>()
}
fn main() {
assert_eq!(3, array_size!(&[1, 2, 3]));
assert_eq!(13, array_size!(b"Hello, world!"));
}
It uses a const generic function size_of_val<T> to determine the type and thus the size of a value passed by reference (the built-in std::mem::size_of_val isn't const).
Note: This doesn't work for arrays of size 0. This can be fixed by using size_of_val($x) / size_of_val(unsafe { &*$x.as_ptr() }) at the cost of wrongly accepting non-array types (e.g. &String).
I want to create array like this:
let arr = [0; length];
Where length is a usize. But I get this error
E0307
The length of an array is part of its type. For this reason, this length
must be a compile-time constant.
Is it possible to create array with dynamic length? I want an array, not a Vec.
Is it possible to create array with dynamic length?
No. By definition, arrays have a length defined at compile time. A variable (because it can vary) is not known at compile time. The compiler would not know how much space to allocate on the stack to provide storage for the array.
You will need to use a Vec:
let arr = vec![0; length];
See also:
Is it possible to control the size of an array using the type parameter of a generic?
This should be possible after variable length arrays (VLA) are implemented.
You can create your own HeapArray. It's not that complicated if you read alloc's docs:
use std::alloc::{alloc, dealloc, Layout};
pub struct HeapArray<T> {
ptr: *mut T,
len: usize,
}
impl<T> HeapArray<T> {
pub fn new(len: usize) -> Self {
let ptr = unsafe {
let layout = Layout::from_size_align_unchecked(len, std::mem::size_of::<T>());
alloc(layout) as *mut T
};
Self { ptr, len }
}
pub fn get(&self, idx: usize) -> Option<&T> {
if idx < self.len {
unsafe { Some(&*(self.ptr.add(idx))) }
} else {
None
}
}
pub fn get_mut(&self, idx: usize) -> Option<&mut T> {
if idx < self.len {
unsafe { Some(&mut *(self.ptr.add(idx))) }
} else {
None
}
}
pub fn len(&self) -> usize {
self.len
}
}
impl<T> Drop for HeapArray<T> {
fn drop(&mut self) {
unsafe {
dealloc(
self.ptr as *mut u8,
Layout::from_size_align_unchecked(self.len, std::mem::size_of::<T>()),
)
};
}
}
impl<T> std::ops::Index<usize> for HeapArray<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
self.get(index).unwrap()
}
}
impl<T> std::ops::IndexMut<usize> for HeapArray<T> {
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
self.get_mut(index).unwrap()
}
}
You may also add methods like as_slice, get_unchecked, etc.
I am trying to have a struct which has a field which I assume should be of type Result<TempDir>. When I initialise an implementation of the field with new(), I would like that particular field to be initialised by the creation of a new temp directory. Later, I want to implement a method to read from that directory.
Here's the code, I am more worried about the syntax and proper use of libraries (why exactly are there over four libraries for read/write buffering in Rust, this is insane) as the logic should be right. Dont worry too much about the trait implementations, I just need directions in the syntax. Please don't be too harsh, as I know it doesn't compile, but with just two changes it should.
extern crate rustc_back;
use std::path::Path;
use std::fs::File;
use rustc_back::tempdir::TempDir as TempDir;
pub struct MyStorage {
temp_dir : Result<TempDir>
}
impl MyStorage {
pub fn new() -> MyStorage {
//tempo = match TempDir::new("encrypt_storage");
let store = match TempDir::new("encrypt_storage") {
Ok(dir) => dir,
Err(e) => panic!("couldn't create temporary directory: {}", e)
};
MyStorage { temp_dir: store }
//MyStorage { temp_dir: TempDir::new("encrypt_storage") }
}
}
impl Storage for MyStorage {
fn get(&self, name: Vec<u8>) -> Vec<u8> {
//let mut f = std::fs::File::open(self.temp_dir.path() / name);
let mut f = std::fs::File::open(&self.temp_dir){
// The `desc` field of `IoError` is a string that describes the error
Err(why) => panic!("couldn't open: {}", why.description()),
Ok(file) => file,
};
let mut s = String::new();
//f.read_to_string(&mut s);
match f.read_to_string(&mut s){
Err(why) => panic!("couldn't read: {}", why.description()),
Ok(_) => print!("contains:\n{}", s),
}
s.to_vec()
}
fn put(&mut self, name: Vec<u8>, data: Vec<u8>) {
// self.entries.push(Entry { name : name, data : data })
let mut f = File::create(self.temp_dir.path() / name);
f.write_all(data);
}
fn put(&mut self, name: Vec<u8>, data: Vec<u8>) {
// self.entries.push(Entry { name : name, data : data })
let mut f = File::create(self.temp_dir.path() / name);
f.write_all(data);
}
}
After fixing the indentation (Rust uses 4 spaces per level), removing the Storage for since you didn't provide that trait, removing commented-out code, and adding a main, you are left with this:
extern crate rustc_back;
use std::path::Path;
use std::fs::File;
use rustc_back::tempdir::TempDir as TempDir;
pub struct MyStorage {
temp_dir : Result<TempDir>
}
impl MyStorage {
pub fn new() -> MyStorage {
let store = match TempDir::new("encrypt_storage") {
Ok(dir) => dir,
Err(e) => panic!("couldn't create temporary directory: {}", e)
};
MyStorage { temp_dir: store }
}
}
impl MyStorage {
fn get(&self, name: Vec<u8>) -> Vec<u8> {
let mut f = std::fs::File::open(self.temp_dir.path() / name);
let mut s = String::new();
f.read_to_string(&mut s);
s.to_vec()
}
fn put(&mut self, name: Vec<u8>, data: Vec<u8>) {
let mut f = File::create(self.temp_dir.path() / name);
f.write_all(data);
}
}
fn main() {}
Compiling that has this error:
error: wrong number of type arguments: expected 2, found 1 [E0243]
temp_dir : Result<TempDir>
^~~~~~~~~~~~~~~
Which nicely points to the problematic type. Let's look at the docs for Result, which includes the definition:
pub enum Result<T, E> {
Ok(T),
Err(E),
}
So Result has two type parameters - T is used for the success case, and E is used for the failure case. Your code is only specifying one of them. My guess is that you looked at the docs for TempDir and copy-and-pasted:
fn new(prefix: &str) -> Result<TempDir>
However, if you click on the Result there, you'll see it goes to io::Result, which is simply a type alias that binds E to io::Error:
type Result<T> = Result<T, Error>;
With all that exposition out of the way, you can "fix" your problem by changing your MyStorage struct:
pub struct MyStorage {
temp_dir: std::io::Result<TempDir>,
}
And then you will get another compiler error, as you are already dealing with the Result via the match in MyStorage::new. You aren't storing a io::Result<TempDir>, you are just storing a TempDir! Changing your struct further:
pub struct MyStorage {
temp_dir: TempDir,
}
unlocks a whole new set of errors for you to figure out; but now you have gotten past that first hurdle!
I want to create an array of a size specified by an element in an enum, like so:
pub use self::Register::*;
enum Register {
Ip,
Sp,
NumRegs,
}
struct State {
val: int,
regs: [int; NumRegs as int],
running: bool,
}
But I get:
src/main.rs:19:11: 19:32 error: expected constant expr for array length: non-constant path in constant expr
src/main.rs:19 regs: [int; NumRegs as int],
I've tried using as int, among other solutions, and Googled around for some time, but have not found a solution. Incidentally, is this bad form in Rust?
Currently, I don't think Rust is capable of seeing that Enum::Variant is effectively a constant (I don't actually know that it is, either), so it cannot be used as the array length. More than that, I would say that it's strange to add an enum variant that isn't actually intended to be used.
For how I see your problem, I'd probably try to represent your registers as a struct:
struct Registers {
ip: u8,
sp: u8,
}
struct State {
val: u8,
regs: Registers,
running: bool,
}
fn main() {
let s = State {
val: 0,
regs: Registers { ip: 0, sp: 0 },
running: false,
};
}
Edit
If you'd like to get a register by name, how about we do that directly:
struct Registers {
ip: u8,
sp: u8,
}
impl Registers {
fn by_name(&self, name: &str) -> u8 {
match name {
"ip" => self.ip,
"sp" => self.sp,
_ => panic!("Unknown register '{}'", name),
}
}
fn by_name_mut(&mut self, name: &str) -> &mut u8 {
match name {
"ip" => &mut self.ip,
"sp" => &mut self.sp,
_ => panic!("Unknown register '{}'", name),
}
}
}
fn main() {
let mut r = Registers { ip: 0, sp: 0 };
println!("Instruction pointer: 0x{:02x}", r.by_name("ip"));
*r.by_name_mut("ip") += 1;
println!("Instruction pointer: 0x{:02x}", r.by_name("ip"));
}
Although that panic! right there is pretty ugly... I'd rather use an enum for that purpose. Let's do both the enum and the string:
use std::str::FromStr;
#[derive(Debug,Copy,Clone,PartialEq)]
enum Register {
Ip,
Sp,
}
impl FromStr for Register {
type Err = ();
fn from_str(s: &str) -> Result<Self, ()> {
match s {
"ip" => Ok(Register::Ip),
"sp" => Ok(Register::Sp),
_ => Err(()),
}
}
}
struct Registers {
ip: u8,
sp: u8,
}
impl Registers {
fn by_name(&self, name: Register) -> u8 {
match name {
Register::Ip => self.ip,
Register::Sp => self.sp,
}
}
fn by_name_mut(&mut self, name: Register) -> &mut u8 {
match name {
Register::Ip => &mut self.ip,
Register::Sp => &mut self.sp,
}
}
}
fn main() {
let mut rs = Registers { ip: 0, sp: 0 };
let r: Register = "ip".parse().unwrap();
println!("Instruction pointer: 0x{:02x}", rs.by_name(r));
*rs.by_name_mut(r) += 1;
println!("Instruction pointer: 0x{:02x}", rs.by_name(r));
}
Now we have a nice clear separation between transitioning a string to our Register type, which means that only one part of our code has to deal with invalid register names (the call to unwrap).
What's wrong with this code?
use std::collections::{HashSet,HashMap};
struct Mod_allfun<'r> {
s: HashMap<&'r str, HashSet<&'r str>>
}
impl <'r>Mod_allfun<'r>{
fn new() -> HashMap<&'r str,HashSet<&'r str>> {HashMap::new()}
fn insert(&mut self, c: &'r str, a:&'r [&'r str]){
let aa: HashSet<&'r str>=a.iter().map(|&x| x).collect() ;
self.s.insert( c , aa );
}
}
fn main() {
let z=Mod_allfun::new();
z.insert("str1", ["str2","str3"] );
}
I have no idea why this does not work as expected!
This for example does work:
use std::collections::{HashSet,HashMap};
fn main() {
let mut mod_allfun: HashMap<& str,HashSet<& str>>= HashMap::new();
let c="str1";
let a=["str2","str3"];
let b = ||{
mod_allfun.insert( c, a.iter().map(|&x| x).collect());
};
}
You're returning a HashMap from new, not a Mod_allfun. This does compile:
use std::collections::{HashSet,HashMap};
struct ModAllfun<'r> {
s: HashMap<&'r str, HashSet<&'r str>>
}
impl<'r> ModAllfun<'r>{
// return a ModAllfun, not a HashMap
fn new() -> ModAllfun<'r> {
ModAllfun { s: HashMap::new() }
}
fn insert(&mut self, c: &'r str, a:&'r [&'r str]){
let aa: HashSet<&'r str> = a.iter().map(|&x| x).collect() ;
self.s.insert(c , aa);
}
}
fn main() {
let mut z = ModAllfun::new();
// pull the array out into a variable to extend its lifetime
let arrs = ["str2","str3"];
z.insert("str1", arrs);
}