Check if Option<String> is Some(my_string) - rust

I'd like to know what is the most idiomatic way of checking if a q: Option<String> that I have has the value of a particular string that I have in my_string: String. So the most straightforward solution is:
if q.is_some() && q.unwrap() == my_string
Another one I can think of:
if q.unwrap_or_default() == my_string
but this wouldn't work in the corner case of my_string being empty.
Another one:
match q {
Some(s) if s == my_string => {
...
},
_ => {},
}
but this is very verbose.
Is there something simpler, like some clever if let?

check it directly:
if Some(my_string) == q {
}
or (to keep my_string alive)
if Some(&my_string) == q.as_ref() {
}
There will be (probably) a contains() function in future rust versions which can be used like
if q.contains(&my_string) {
}
It is more flexible because it allows to compare different datatypes (when they implement PartialEq). See https://github.com/rust-lang/rust/issues/62358

I really like the answer provided by #ensc but it consumes my_string; here is a version that does not.
fn main() {
let my_string = String::from("abcd");
let q1: Option<String> = Some(String::from("abcd"));
let q2: Option<String> = Some(String::from("abc"));
let q3: Option<String> = None;
// if Some(my_string) == q1 { // this will CONSUME my_string
if Some(&my_string) == q1.as_ref() {
println!("The same {:?}", q1);
}
// if Some(my_string) == q2 { // this will CONSUME my_string
if Some(&my_string) == q2.as_ref() {
println!("The same {:?}", q2);
}
// if Some(my_string) == q3 { // this will CONSUME my_string
if Some(&my_string) == q3.as_ref() {
println!("The same {:?}", q3);
}
}

Just use if let:
fn main() {
let s = Some("abc".to_string());
if let Some("abc") = s.as_ref().map(|s: &String| s as &str) {
println!("Ok")
}
}

On the Rust book :
let some_u8_value = Some(0u8);
match some_u8_value {
Some(3) => println!("three"),
_ => (),
}
Is equivalent of :
if let Some(3) = some_u8_value {
println!("three");
}

Related

How to skip n items from inside of an iterator loop?

This code:
play
fn main() {
let text = "abcd";
for char in text.chars() {
if char == 'b' {
// skip 2 chars
}
print!("{}", char);
}
// prints `abcd`, but I want `ad`
}
prints abcd, but I want to skip 2 chars if b was found, so that it prints ad. How do I do that?
I tried to put the iterator into a variable outside the loop and manipulate that iterator within the loop, but the Borrow Checker doesn't allow that.
AFAIK you can't do that with a for loop. You will need to desugar it by hand:
let mut it = text.chars();
while let Some(char) = it.next() {
if char == 'b' {
it.nth(1); // nth(1) skips/consumes exactly 2 items
continue;
}
print!("{}", char);
}
Playground
If you want to keep an iterator style, you can use std::iter::successors (I've replaced the special char with '!' for being more readable:
fn my_iter<'a>(s: &'a str) -> impl Iterator<Item = char> + 'a {
let mut it = s.chars();
std::iter::successors(it.next(), move |c| {
if *c == '!' {
it.next().and_then(|_| it.next())
} else {
it.next()
}
})
.filter(|c| *c != '!')
}
fn main() {
assert!(my_iter("a!bc").eq("ac".chars()));
assert!(my_iter("!abcd").eq("bcd".chars()));
assert!(my_iter("abc!d").eq("abc".chars()));
assert!(my_iter("abcd!").eq("abcd".chars()));
}

Consolidating multiple copies of a character at start of string into one in Rust

I'm working on a parser for a mini language, and I have the need to differentiate between plain strings ("hello") and strings that are meant to be operators/commands, and start with a specific sigil character (e.g. "$add").
I also want to add a way for the user to escape the sigil, in which a double-sigil gets consolidated into one, and then is treated like a plain string.
As an example:
"hello" becomes Str("hello")
"$add" becomes Operator(Op::Add)
"$$add" becomes Str("$add")
What would be the best way to do this check and manipulation? I was looking for a method that counts how many times a character appears at the start of a string, to no avail.
Can't you just use starts_with?
fn main() {
let line_list= [ "hello", "$add", "$$add" ];
let mut result;
for line in line_list.iter() {
if line.starts_with("$$") {
result = line[1..].to_string();
}
else if line.starts_with("$") {
result = format!("operator:{}", &line[1..]);
}
else {
result = line.to_string();
}
println!("result = {}", result);
}
}
Output
result = hello
result = operator:add
result = $add
According to the comments, your problem seems to be related to the access to the first chars.
The proper and efficient way is to get a char iterator:
#[derive(Debug)]
enum Token {
Str(String),
Operator(String),
}
impl From<&str> for Token {
fn from(s: &str) -> Self {
let mut chars = s.chars();
let first_char = chars.next();
let second_char = chars.next();
match (first_char, second_char) {
(Some('$'), Some('$')) => {
Token::Str(format!("${}", chars.as_str()))
}
(Some('$'), Some(c)) => {
// your real handling here is probably different
Token::Operator(format!("{}{}", c, chars.as_str()))
}
_ => {
Token::Str(s.to_string())
}
}
}
}
fn main() {
println!("{:?}", Token::from("π"));
println!("{:?}", Token::from("hello"));
println!("{:?}", Token::from("$add"));
println!("{:?}", Token::from("$$add"));
}
Result:
Str("π")
Str("hello")
Operator("add")
Str("$add")
playground

How to implement a lightweight long-lived thread based on a generator or asynchronous function in Rust?

I want to implement a user interaction script in the form of a lightweight, long-lived thread written in Rust. Inside the script, I have points where I asynchronously await user input.
In JavaScript, I would use a generator, inside which you can pass a question, and get back an answer, for example:
function* my_scenario() {
yield "What is your name?";
let my_name = yield "How are you feeling?";
let my_mood = yield "";
...
}
let my_session = my_scenario();
...
my_session.next("Peter");
my_session.next("happy");
However, Rust's generator method resume() contains no parameters! I cannot clone a generator or return it from a function in order to have many user sessions with different states. Instead of a generator, I thought of using an async fn(), but I do not understand how to call it at each step, passing the value there.
The return value from yield is effectively just another generator that has been implicitly passed to the first generator, except that it forces the two to be tied together in weird ways.
You can see that in your original code by the junk yield "" that you need in order to get a value even though you don't have anything to return. Additionally, your example requires that the user of the generator know the answer to the question before it is asked, which seems very unorthodox.
Explicitly pass in a second generator:
#![feature(generators, generator_trait)]
use std::{
io,
ops::{Generator, GeneratorState},
};
fn user_input() -> impl Generator<Yield = String> {
|| {
let input = io::stdin();
loop {
let mut line = String::new();
input.read_line(&mut line).unwrap();
yield line;
}
}
}
fn my_scenario(
input: impl Generator<Yield = String>,
) -> impl Generator<Yield = &'static str, Return = String> {
|| {
let mut input = Box::pin(input);
yield "What is your name?";
let my_name = match input.as_mut().resume(()) {
GeneratorState::Yielded(v) => v,
GeneratorState::Complete(_) => panic!("input did not return a value"),
};
yield "How are you feeling?";
let my_mood = match input.as_mut().resume(()) {
GeneratorState::Yielded(v) => v,
GeneratorState::Complete(_) => panic!("input did not return a value"),
};
format!("{} is {}", my_name.trim(), my_mood.trim())
}
}
fn main() {
let my_session = my_scenario(user_input());
let mut my_session = Box::pin(my_session);
loop {
match my_session.as_mut().resume(()) {
GeneratorState::Yielded(prompt) => {
println!("{}", prompt);
}
GeneratorState::Complete(v) => {
println!("{}", v);
break;
}
}
}
}
$ cargo run
What is your name?
Shep
How are you feeling?
OK
Shep is OK
You can provide hard-coded data as well:
let user_input = || {
yield "Peter".to_string();
yield "happy".to_string();
};
let my_session = my_scenario(user_input);
As of approximately Rust nightly 2020-02-08, Rust's generators now accept an argument to resume, more closely matching the original JavaScript example:
#![feature(generators, generator_trait)]
use std::{
io::{self, BufRead},
ops::{Generator, GeneratorState},
};
fn my_scenario() -> impl Generator<String, Yield = &'static str, Return = String> {
|_arg: String| {
let my_name = yield "What is your name?";
let my_mood = yield "How are you feeling?";
format!("{} is {}", my_name.trim(), my_mood.trim())
}
}
fn main() {
let my_session = my_scenario();
let mut my_session = Box::pin(my_session);
let stdin = io::stdin();
let mut lines = stdin.lock().lines();
let mut line = String::new();
loop {
match my_session.as_mut().resume(line) {
GeneratorState::Yielded(prompt) => {
println!("{}", prompt);
}
GeneratorState::Complete(v) => {
println!("{}", v);
break;
}
}
line = lines.next().expect("User input ended").expect("User input malformed");
}
}

How do I return a struct or anything more complicated than a primitive?

I've been tinkering with Rust and I'm a little confused with function return types. As an experiment I'm writing an IRC log parser. I'm familiar with the primitive types, and having functions return those. What about more complex types when returning multiple pieces of data?
/* Log line example from log.txt */
/* [17:35] <#botname> name1 [460/702] has challenged name2 [224/739] and taken them in combat! */
#[derive(Show)]
struct Challenger {
challenger: String,
defender: String
}
fn main() {
let path = Path::new("log.txt");
let mut file = BufferedReader::new(File::open(&path));
for line in file.lines() {
let mut unwrapped_line = line.unwrap();
let mut chal = challenges3(unwrapped_line);
println!("Challenger: {}", chal.challenger);
println!("Defender: {}", chal.defender);
}
}
fn challenges3(text: String)-> Challenger {
let s: String = text;
let split: Vec<&str> = s.as_slice().split(' ').collect();
if(split[4] == "has" && split[5] == "challenged") {
let mychallenger = Challenger { challenger: split[2].to_string(), defender: split[6].to_string()};
return mychallenger;
}
}
I realize this code isn't very idiomatic, I'm getting familiar with the language.
I get an error with this code:
"mismatched types: expected `Challenger`, found `()` (expected struct Challenger, found ())"
How can I return a Struct or a HashMap? Is there a better way to return multiple fields of data?
The if in challenges3 has no else block, so if the condition isn't met, execution continues after the if block. There's nothing there, so the function implicitly returns () at this point. You must also return a Challenger after the if block, or panic! to abort the program.
Alternatively, you could change the return type of your function to Option<Challenger>. Return Some(mychallenger) in the if block, and None after the if block:
fn challenges3(text: String) -> Option<Challenger> {
let s: String = text;
let split: Vec<&str> = s.as_slice().split(' ').collect();
if split[4] == "has" && split[5] == "challenged" {
let mychallenger = Challenger { challenger: split[2].to_string(), defender: split[6].to_string()};
return Some(mychallenger);
}
None
}
You can also use Result instead of Option if you want to return some information about the error.

In Rust, what is the best way to print something between each value in a container?

I want to print every item in a vector separated by commas. You could use numeric indexing:
for i in 0..vec.len() {
print!("{}", vec[i]);
if i < vec.len() - 1 {
print!(", ");
}
}
But what if you just have an Iterator? You either need to treat the first or last value specially, or create a custom iterator, which seems like a lot of work.
Is there a cleaner idiomatic way of expressing this in Rust?
If you want to avoid using a variable to check if element is first, you can make use of .take() and .skip() methods of iterators:
for e in vec.iter().take(1) {
print!("{}", e);
}
for e in vec.iter().skip(1) {
print!(", {}", e);
}
or compact all in a fold :
vec.iter().fold(true, |first, elem| {
if !first { print(", "); }
print(elem);
false
});
You can do something special for the first element, then treat all subsequent ones the same:
let mut iter = vec.iter();
if let Some(item) = iter.next() {
print!("{}", item);
for item in iter {
print!("<separator>{}", item);
}
}
If you use Itertools::format, it's even easier:
println!("{}", vec.iter().format("<separator>"));
let first = true;
for item in iterator {
if !first {
print(", ");
}
print(item);
first = false;
}
Another method:
fn main() {
let a = vec!["May", "June"];
for (n, s) in a.iter().enumerate() {
if n > 0 {
print!(",");
}
print!("{}", s);
}
}
https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.enumerate

Resources