How to write multiple condition in if let statement? - rust

How to modify if let statement so it also handles another condition like Some(7) == b?
let a = Some(6);
let b = Some(7);
if let Some(6) = a /* && Some(7) = b */{
// do something
}

You can use a simple tuple:
if let (Some(6), Some(7)) = (a, b) {
// do something
}

The if let expression only admits one match arm pattern against one expression. However, they can be merged together into a single equivalent pattern match condition. In this case, the two Option values can be combined into a pair and matched accordingly.
if let (Some(6), Some(7)) = (a, b) {
// do something
}
Playground
See also:
Simplest way to match multiple fields of a struct against `None`

Related

Check serde_json Value for value with key sets of arbitrary depth or add if null Rust

With something like the vec below Id like to add arbitrary depth to a json object.
let set = vec![vec!["123","apple","orange","999"],vec!["1234","apple"],vec!["12345","apple","orange"]];
Once created the above would look something like:
{"123":{"apple":{"orange":"999"}}, "1234":"apple", "12345":{"apple":"orange"}}
Ive tried recursion, the issue Im running into is that Im having trouble reasoning through it. The wall Ive hit is how do I refer up the chain of values?
Is there a method Im missing here? Surely Im not the only person whos wanted to do this...
I would prefer if at all possible not writing something cumbersome that takes the length of a key set vec and matches creating the nesting ex.:
match keys.len() {
2 => json_obj[keys[0]] = json!(keys[1]),
3 => json_obj[keys[0]][keys[1]] = json!(keys[2]),
4 => json_obj[keys[0]][keys[1]][keys[2]] = json!(keys[3]),
...
_=> ()
}
Any ideas?
You can do this with iteration -- each loop you walk deeper into the structure, and further into the iterator, but the trick is that each step you need to know if there are more elements beyond the final one because the final element needs to be a string instead of an object. We'll do this using a match construct that matches on the next two items in the sequence at once.
We can further generify the function to take "anything that can be turned into an iterator that produces items from which we can obtain a &str". This will accept both an iterator of String or an iterator of &str, for example, or even directly a Vec of either.
use std::borrow::Borrow;
use serde_json::Value;
fn set_path(
mut obj: &mut Value,
path: impl IntoIterator<Item=impl Borrow<str>>
) {
let mut path = path.into_iter();
// Start with nothing in "a" and the first item in "b".
let mut a;
let mut b = path.next();
loop {
// Shift "b" down into "a" and put the next item into "b".
a = b;
b = path.next();
// Move "a" but borrow "b" because we will use it on the next iteration.
match (a, &b) {
(Some(key), Some(_)) => {
// This level is an object, rebind deeper.
obj = &mut obj[key.borrow()];
}
(Some(s), None) => {
// This is the final string in the sequence.
*obj = Value::String(s.borrow().to_owned());
break;
}
// We were given an empty iterator.
(None, _) => { break; }
}
}
}
(Playground)

Rust Lang: What is "if let Some(x) = x" doing?

I'm working on a rust project written a couple of years ago, and have come across this piece of code, which is literally:
let mut values = vec![];
for x in maybe_values {
if let Some(x) = x {
values.push(Arc::new(x));
}
}
I understand that "if let" introduces a pattern-matching if (Which seems to be a poor re-use of the keyword "let", but I will get over that - If anyone can help me with a mental mnemonic to make sense of "let" here, please do!).
But what is the test Some(x) = x doing?
From my testing, it seems to be a trick/idiom to both a) test that the loop variant 'x' is Some(), and also b) end up with the unwrap()ped value in x.
But I can't fully explain it to myself, and can't find reference to this being an idiom anywhere.
Hope you can help my Rust education path. Thanks.
This is a shorthand for using a full match statement when you only care about matching a single use case.
So this block of code:
if let x = y {
foo();
} else {
bar();
}
Is equivalent to using a full match:
match y {
x => {
foo();
}
_ => {
bar();
}
}
For your specific case, it is equivalent to this. The inner x uses the same name as the outer variable which can be confusing, but they are two separate values.
let mut values = vec![];
for x in maybe_values {
match x {
Some(y) => values.push(Arc::new(y)),
_ => {},
}
}
There are two completely different variables in play here. It's equivalent to.
let mut values = vec![];
for x_1 in maybe_values {
if let Some(x_2) = x_1 {
values.push(Arc::new(x_2));
}
}
In Rust, the right-hand side of a let is evaluated with the left-hand variable not in scope, so when the if let is evaluated, the outer x is still in-scope. Then, if it's a Some value, we make a new variable x which contains the inside of the Option. This variable shadows the previous x, making it inaccessible inside the if statement (in the same way that a function argument called x would render a global variable named x inaccessible by shadowing).

Ocaml struct like we do in C

I want to write a recursive struct, like we do in C, but in Ocaml.
I want we contains a boolean and a list of recursive copies of elements of the same type.
How can I do that in Ocaml?
OCaml type declarations are recursive by default. So there's nothing special to do to get something like you describe. If you literally want a list inside your struct, you can define something like this:
type mystruct = { b: bool; others: mystruct list }
If you want a "linked list" of structures of your given type, you could define something more like this:
type mystruct2 = { b: bool; rest : mystruct2 option }
One key here is that the rest element is an option type so that your list can end at some point.
There are many other ways to define this basic structure. A more idiomatic OCaml definition might be to use a variant type:
type mylist = Last | Node of bool * mylist
This last type isn't exactly equivalent because a value of your described type always contains at least one boolean, and this type has a value (Last) with no booleans. In practice this usually turns out to be what you want.
Update
Here are two top-level variables of each of these types. In each pair the first is as small as possible and the second is a little larger.
let v10 = { b = true; others = []}
let v11 = { b = false; others = [ {b = true; others = []} ] }
let v20 = { b = true; rest = None }
let v21 = { b = false; rest = Some { b = true; rest = None } }
let v30 = Last
let v31 = Node (true, Last)
If you have basic questions like this, it might be worthwhile to work through an OCaml tutorial, maybe one of those listed here: https://ocaml.org/learn/tutorials/

Is there a way to pass named arguments to format macros without repeating the variable names?

With new versions of Rust, you can simplify structure initialization like this:
Foo {
a: a,
b: b,
}
to this
Foo { a, b }
Is it possible to do something similar for format!/println!-like macros?
For now I need to write it like this:
let a = "a";
let b = "b";
write!(file, "{a} is {b}", a = a, b = b).unwrap();
Is it possible to write my own macros with an API like this:
let a = "a";
let b = "b";
my_write!(file, "{a} is {b}", a, b).unwrap();
RFC 2795 has been accepted and implemented. Starting in Rust 1.58, you will be able to go beyond your desired syntax:
write!(file, "{a} is {b}").unwrap();
Before then, you can write your own wrapper around println! and friends:
macro_rules! myprintln {
($fmt:expr, $($name:ident),*) => { println!($fmt, $($name = $name),*) }
}
fn main() {
let a = "alpha";
let b = "beta";
myprintln!("{a} is {b}", a, b);
}
This will likely always be limited compared to the full formatter macro, but it may be sufficient for your case.
As of 1.5 this is the closest you can get natively.
my_write!(file, "{} is {}", a, b).unwrap();
Additionally as Shepmaster pointed out my answer will eventually become obsolete (I'll try to remember to remove it when that happens). If you want to keep an eye on the (to be implemented) syntax they propose you can check this github ticket they linked

Assign a single value to multiple variables in one line in Rust?

A common way to assign multiple variables is often expressed in programming languages such as C or Python as:
a = b = c = value;
Is there an equivalent to this in Rust, or do you need to write it out?
a = value;
b = value;
c = value;
Apologies if this is obvious, but all my searches lead to Q&A regarding tuple assignment.
No, there is no equivalent. Yes, you have to write multiple assignments, or write a macro which itself does multiple assignments.
You cannot chain the result of assignments together. However, you can assign multiple variables with a single statement.
In a let statement, you can bind multiple names by using an irrefutable pattern on the left side of the assignment:
let (a, b) = (1, 2);
(Since Rust 1.59, you can also have multiple values in the left side of any assignment, not just let statements.)
In order to assign the same value to multiple variables without repeating the value, you can use a slice pattern as the left-hand side of the assignment, and an array expression on the right side to repeat the value, if it implements Copy:
let value = 42;
let [a, b, c] = [value; 3]; // or: let [mut a, mut b, mut c] = ...
println!("{} {} {}", a, b, c); // prints: 42 42 42
(Playground)
Using const generics:
fn main() {
let [a, b, c] = fill_new_slice(1);
dbg!(a, b, c);
}
fn fill_new_slice<T: Copy, const N: usize>(value: T) -> [T; N] {
[value; N]
}
$ cargo run --quiet
[src/main.rs:3] a = 1
[src/main.rs:3] b = 1
[src/main.rs:3] c = 1
Actually, you can totally do this!
let a # b # c = value;
This uses the # syntax in patterns, which is used to bind a value to a variable, but keep pattern matching. So this binds value to a (by copy), and then continues to match the pattern b # c, which binds value to b, and so on.
But please don't. This is confusing and of little to no benefit over writing multiple statements.
In Rust, the expression a = b = c = value; is the same to a = (b = (c = value));
And the (x = ...) returns (). Then, the first expression is an equivalent of the following:
c = value;
b = ();
a = ();
Note that the expression has a semicolon in the end, but if the expression were in the last line as a function's return like this a = b = c = value, the equivalente would be the following:
c = value;
b = ();
a = () // without the semicolon

Resources