How to define a macro vecvec to initialize a vector of vectors? - rust

Just like vec![2,3,4], can we define a similar macro vecvec to initialize vector of vector. Eg.
let vv0 = vecvec![[2,3,4],[5,6,7]]; // vec of 2 vecs
let vv1 = vecvec![[1,2,3]];
let vv2 = vecvec![[1,2,3], []];
let vv3 = vecvec![[1,3,2]; 2];

You just need to think through the problem. You really only have 2 main cases. The first case being if elements are listed (Ex: a, b, c) and the second where a single value and length are given (Ex: a; b). We can even check our work by reading the documentation for vec!. In the documentation we can see vec! is defined as follows:
macro_rules! vec {
() => { ... };
($elem:expr; $n:expr) => { ... };
($($x:expr),+ $(,)?) => { ... };
}
As you can see, they have 3 cases. We didn't specify the the case were no items are included, but that does not really matter since your macro can call vec! and have it handle that case for you.
We can just copy the cases in their macro and add the functionality inside. The only other issue that might stop you is that [a, b, c] is an expression in of itself. Luckily we can just skip that by specifying items as requiring brackets and pick out the items ourselves before passing them off to vec!.
macro_rules! vecvec {
([$($elem:expr),*]; $n:expr) => {{
let mut vec = Vec::new();
vec.resize_with($n, || vec![$($elem),*]);
vec
}};
($([$($x:expr),*]),* $(,)?) => {
vec![$(vec![$($x),*]),*]
};
}

Instead of defining a new macro. You can initialize the vector of the vector.
In the example below, I'm explicitly setting type. It's not necessary but a good practice.
let vv0:Vec<Vec<u32>> = vec![vec![2,3,4],vec![5,6,7]];
let vv1:Vec<Vec<u32>> = vec![vec![2,3,4],vec![5]];
let vv2:Vec<Vec<u32>> = vec![vec![],vec![5,6,7]];
let vv3:Vec<Vec<u32>> = vec![vec![2,3,4],vec![]];

Related

Rust lifetimes in if statement

I have an if statement in a for loop, and I want it to create a variable with the lifetime of that iteration of the for loop.
for condition_raw in conditions_arr {
println!("{}", condition_raw);
let matching = !condition_raw.contains('!');
if matching {
let index = condition_raw.find('=').unwrap_or_default();
} else {
let index = condition_raw.find('!').unwrap_or_default();
}
let key = &condition_raw[..index];
}
let key = &condition_raw[..index]; currently throws cannot find value index in this scope
not found in this scope rustc E0425
I'll ignore the condition variable which does not seem to be used at all in your example.
A let statement creates a binding that holds at most for the current scope. For this reason, when you create the index variable inside the if, you are not making it accessible anywhere else. There are two ways to solve this issue.
The first way is to explicitly declare index as being part of the outer scope, and only define it inside the if statement.
for condition_raw in conditions_arr {
let matching = !condition_raw.contains('!');
let index;
if matching {
index = condition_raw.find('=').unwrap_or_default();
} else {
index = condition_raw.find('!').unwrap_or_default();
}
let key = &condition_arr[..index];
}
There is no risk of accidentally not defining index, since Rust will make sure that index is defined (exactly once) in all possible branching of your code before it is used. Yet, it's not a pretty solution because it violates a "locality" principle, that is that pieces of code should have effects on or pieces of code that are sufficiently close. In this case, the let index; is not too far from its definition, but it could be arbitrarily far, which makes it painful for someone who reads your code to remember that there is a declared but not yet defined.
Alternatively, you could use the fact that most things in Rust are expressions:
for condition_raw in conditions_arr {
let matching = !condition_raw.contains('!');
let index = if matching {
condition_raw.find('=').unwrap_or_default();
} else {
condition_raw.find('!').unwrap_or_default();
}
let key = &condition_arr[..index];
}
But, in fact, you could factorize your code even more, which is usually better:
for condition_raw in conditions_arr {
let matching = !condition_raw.contains('!');
let index = condition_raw.find(if matching {
'='
} else {
'!'
}).unwrap_or_default();
let key = &condition_arr[..index];
Or, even more
for condition_raw in conditions_arr {
let index = condition_raw
.find('!')
.or_else(|| condition_raw.find('='))
.unwrap_or_default();
let key = &condition_arr[..index];
}
An idiomatic way to assign variables from an if else statement is as follows:
let index: usize = if matching {
condition_raw.find('=').unwrap_or_default()
} else {
condition_raw.find('!').unwrap_or_default()
};
Idiomatic way of assigning a value from an if else condition in Rust
In Rust, an if/else block is an expression. That is to say, the block itself has a value, equivalent to the last expression in whatever section was executed. With that in mind, I would structure your code like this:

Is there an idiomatic way of chaining Option in Rust?

When deserializing deeply nested structures (e.g. from JSON), it's not uncommon to have to traverse multiple Option types.
For example:
let foo = Foo {
x: Some(Bar {
y: Some(Baz {
z: Some(42),
})
})
};
Is there an idiomatic way of chaining Option to access deeply nested values?
So far I have the following, but neither are as concise as foo.x?.y?.z in other languages that support optional-chaining:
let z = foo.x.as_ref().and_then(|x| x.y.as_ref()).and_then(|y| y.z);
let z = foo.x.as_ref().and_then(|x| x.y.as_ref()?.z);
let z = (|| foo.x.as_ref()?.y.as_ref()?.z)();
It looks like the try_block feature might be a good fit, but it's currently unstable.
let z = try { foo.x.as_ref()?.y.as_ref()?.z };
As you say, the try block would be perfect for this.
In the meantime you can take advantage of the fact that ? works in functions, and wrap your expression in a closure and call it:
let z = (|| foo.x.as_ref()?.y.as_ref()?.z )();
You can write a simple macro to make it a bit nicer:
macro_rules! tryit {
($($e: tt)+) => {
(|| { $($e)+ })()
}
}
Which works basically the same as the try block:
let z = tryit! { foo.x.as_ref()?.y.as_ref()?.z };
Another option if you don't like the immediately called closure is to use a crate like map_for or mdo. Example with map_for:
use map_for::option::FlatMap;
let z = map_for!{
x <- foo.x.as_ref();
y <- x.y.as_ref();
=> y.z }:
Disclaimer: I wrote the map_for crate.

error handling when unwrapping several try_into calls

I have a case where I need to parse some different values out from a vector.
I made a function for it, that returns a option, which either should give a option or a None, depending on whether the unwrapping succeeds.
Currently it looks like this:
fn extract_edhoc_message(msg : Vec<u8>)-> Option<EdhocMessage>{
let mtype = msg[0];
let fcnt = msg[1..3].try_into().unwrap();
let devaddr = msg[3..7].try_into().unwrap();
let msg = msg[7..].try_into().unwrap();
Some(EdhocMessage {
m_type: mtype,
fcntup: fcnt,
devaddr: devaddr,
edhoc_msg: msg,
})
}
But, I would like to be able to return a None, if any of the unwrap calls fail.
I can do that by pattern matching on each of them, and then explicitly return a None, if anything fails, but that would a lot of repeated code.
Is there any way to say something like:
"if any of these unwraps fail, return a None?"
This is exactly what ? does. It's even shorter than the .unwrap() version:
fn extract_error_message(msg: Vec<u8>) -> Option<EdhocMessage> {
let m_type = msg[0];
let fcntup = msg[1..3].try_into().ok()?;
let devaddr = msg[3..7].try_into().ok()?;
let edhoc_msg = msg[7..].try_into().ok()?;
Some(EdhocMessage {
m_type,
fcntup,
devaddr,
edhoc_msg
})
}
See this relevant part of the Rust Book.

Why can't this be done with if?

I'm trying to handle errors received from an async call:
let res: Result<TcpStream, Box<dyn std::error::Error>> = session.runtime().borrow_mut().block_on(async {
let fut = TcpStream::connect(session.configuration().socket()).await?;
Ok(fut)
});
I tried to do it the old school way with an if but the compiler didn't like it:
if res.is_err() {
return Err(res);
}
After some googling I came across this:
let mut stream = match res {
Ok(res) => res,
Err(res) => return Err(res),
};
which feels very much the same but with Rusts' equivalent of a switch statement. Why can't I use the if?
if res.is_err() { return res } should work. Result is an enum with two variants: Ok which by convention holds a "successful" result, and Err which holds error information. As John pointed out, wrapping the existing Result (which happens to hold an Err) in another Err result doesn't make sense - or, more precisely, doesn't match the return type of the function.
When you use match, you unpack the result into its constituent values, and then in the error case re-pack it into a new result. Note that instead of the match statement use can use the ? operator, which would compress the declaration to just:
let mut stream = res?;

Can you write a macro to invoke the default() operator in rust?

Something like:
macro_rules! default(
($T:ty, $($args:expr)*) => (
$T { $($args)*, ..Default::default() };
);
)
...but with a magical type instead of expr, so you could do something like:
let p = default!(CItem, _z: ~"Hi2", x: 10);
let q = default!(CItem, _z, ~"Hi2", x, 10);
let r = default!(CItem, { _z: ~"Hi2", x: 10 });
Or something along those lines?
Is there any macro symbol that simply picks up a literal block of characters without first parsing it as a type/expr/etc?
(I realize you'd typically just write a CItem::new(), but this seems like a nice situation in some circumstances)
Macros can have multiple pattern to match the syntax, so you have to write a seperate pattern for every case seperatly like this:
macro_rules! default(
($t:ident, $($n:ident, $v:expr),*) => {
$t { $($n: $v),*, ..Default::default() }
};
($t:ident, $($n:ident: $v:expr),*) => {
default!($t, $($n, $v),*)
};
)
As you can see there are two patterns, the first matching pairs seperated by comma and the second one pairs seperated by colon.

Resources