Rust String vs &str iterators - rust

I'm trying to write a function which accepts a list of tokens. But I'm having problems making it general enough to handle two pretty similar calls:
let s = String::from("-abc -d --echo");
parse( s.split_ascii_whitespace() );
parse( std::env::args() );
String::split_ascii_whitespace() returns std::str:SplitAsciiWhitespace which implements Iterator<Item=&'a str>.
std::env::args() returns std::env::Args which implements Iterator<Item=String>.
Is there a way for me to write a function signature for parse that will accept both methods?
My solution right now requires duplicating function bodies:
fn main() {
let s = String::from("-abc -d --echo");
parse_args( s.split_ascii_whitespace() );
parse_env( std::env::args() );
}
fn parse_env<I: Iterator<Item=String>>(mut it: I) {
loop {
match it.next() {
None => return,
Some(s) => println!("{}",s),
}
}
}
fn parse_args<'a, I: Iterator<Item=&'a str>>(mut it: I) {
loop {
match it.next() {
None => return,
Some(s) => println!("{}",s),
}
}
}
If not possible, then some advice on how to use the traits so the functions can use the same name would be nice.

You can require the item type to be AsRef<str>, which will include both &str and String:
fn parse<I>(mut it: I)
where
I: Iterator,
I::Item: AsRef<str>,
{
loop {
match it.next() {
None => return,
Some(s) => println!("{}", s.as_ref()),
}
}
}

Depending on your use case, you could try:
fn main() {
let s = String::from("-abc -d --echo");
parse( s.split_ascii_whitespace() );
parse( std::env::args() );
}
fn parse<T: std::borrow::Borrow<str>, I: Iterator<Item=T>>(mut it: I) {
loop {
match it.next() {
None => return,
Some(s) => println!("{}",s.borrow()),
}
}
}
I used Borrow as a means to get to a &str, but your concrete use case may be served by other, possibly custom, traits.

Related

Handling a Response from a Result using match

I'm trying to return a Result from a function and extract it's return.
I'm using i32 and a &str and I get a mismatch type error in the match statment (Can't use two different types in a match).
How do I fix this?
fn use_result(par: i32)-> Result<i32, &'static str> {
if par == 0 {
Err("some error")
} else {
println!("par is 1");
Ok(par)
}
}
fn main() {
// Result
let res = match use_result(1) {
Ok(v) => v,
Err(e) => e,
};
}
//Do something with res: v or res: e
}
In Rust, every variable has a single type. In the code you have now, res is either a &'static str or an i32, which is not allowed.
Your options are:
Return early
fn main() {
let res: i32 = match use_result(1) {
Ok(v) => v,
Err(e) => return,
};
}
Different code in each match arm
fn main() {
match use_result(1) {
Ok(v) => {
handle_success(v);
},
Err(e) => {
handle_error(e);
},
};
}
Return an enum
Enums allow you to express that a type is "one of these possible variants" in a type safe way:
enum IntOrString {
Int(i32),
String(&'static str),
}
fn main() {
let i_or_s: IntOrString = match use_result(1) {
Ok(v) => IntOrString::Int(v),
Err(e) => IntOrString::String(e),
};
}
But this is a bit weird, since Result<i32, &'static str> is already an enum, if you want to do anything with an IntOrString you'll need to match on it later on (or an if let, etc).
Panic
fn main() {
let res: i32 = match use_result(1) {
Ok(v) => v,
Err(e) => panic!("cannot be zero"),
};
}
This is more cleanly expressed as use_result(1).unwrap(). It's usually not what you want, since it doesn't allow the caller of the function to recover/handle the error. But if you're calling this from main(), the error has nowhere else to propagate to, so unwrapping is usually OK.

How do I return an error within match statement while implementing from_str in rust?

I'm new to rust.
I'm trying to follow the example for implementing the from_str trait here
https://doc.rust-lang.org/std/str/trait.FromStr.html
But I keep getting this error pointing at 'return Err(Self::Err)'
variant or associated item not found in `black_jack_tools::PlayerDifficulty`
I have an idea of why, Self::Err isn't defined in my enum But I don't get why rust cares in this scenario since I'm returning an Err of my Err object which is inline with the Result<Self,Self::Err> type.
Here's my FromStr is below here's a link to the rust playground with an MRE
impl FromStr for PlayerDifficulty {
type Err = ParseError;
fn from_str(s:&str) -> Result<Self,Self::Err>{
let result = match s {
"Player" => Ok(PlayerDifficulty::Player),
"Dealer" => Ok(PlayerDifficulty::Dealer),
"Normal" => Ok(PlayerDifficulty::Normal),
"Perfect"=> Ok(PlayerDifficulty::Perfect),
"Micky" => Ok(PlayerDifficulty::Micky),
"Elliot" => Ok(PlayerDifficulty::Elliot),
"Cultist"=> Ok(PlayerDifficulty::Cultist),
_ => return Err(Self::Err)
};
}
}
What Am I doing wrong?
Is there a better way to do this?
There are three issues with your code. The first is that you need to use <Self as FromStr>::Err if you want to refer to the Err type in your FromStr implementation:
impl FromStr for PlayerDifficulty {
type Err = ParseError;
fn from_str(s:&str) -> Result<Self,Self::Err>{
let result = match s {
"Player" => Ok(PlayerDifficulty::Player),
/* ... */
_ => return Err(<Self as FromStr>::Err)
};
}
}
Self::Err tries to look for an Err variant in the PlayerDifficulty enum but there is no such variant.
The second issue is that std::string::ParseError is in fact an alias for std::convert::Infallible, which is an error that can never happen and cannot be instantiated. Since your conversion may fail, you need to use an error that can be instantiated or define your own:
struct UnknownDifficultyError;
impl FromStr for PlayerDifficulty {
type Err = UnknownDifficultyError;
fn from_str(s:&str) -> Result<Self,Self::Err>{
let result = match s {
"Player" => Ok(PlayerDifficulty::Player),
/* ... */
_ => return Err(UnknownDifficultyError),
};
}
}
Finally, you need to return the result even when conversion succeeds, by removing the let result = and the semicolon:
struct UnknownDifficultyError;
impl FromStr for PlayerDifficulty {
type Err = UnknownDifficultyError;
fn from_str(s:&str) -> Result<Self,Self::Err>{
match s {
"Player" => Ok(PlayerDifficulty::Player),
/* ... */
_ => return Err(UnknownDifficultyError),
}
}
}
Playground
The function will return it last statement. Remove the last semicolon, and you could also remove the internal return statement, the result of the match statement will be returned.
Is there a better way? It looks like you are parsing a string to a enum, the create enum-utils does that. Instead of implementing the parser with boilerplate code you just derive it.
#[derive(Debug, PartialEq, enum_utils::FromStr)]
enum PlayerDifficulty {
Player,
Dealer,
Cultist,
Normal,
}
fn main() {
let _x:PlayerDifficulty= "Player".parse().unwrap();
}
And in your cargo.toml
[dependencies]
enum-utils = "0.1.2"
You should define a custom error
#[derive(Debug)]
struct PlayerError;
impl std::fmt::Display for PlayerError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Could not parse player")
}
}
impl std::error::Error for PlayerError{}
Then change the match always return the Result in the same path
use std::str::FromStr;
impl FromStr for PlayerDifficulty {
type Err = PlayerError;
fn from_str(s:&str) -> Result<Self,Self::Err>{
match s {
"Player" => Ok(PlayerDifficulty::Player),
"Dealer" => Ok(PlayerDifficulty::Dealer),
"Normal" => Ok(PlayerDifficulty::Normal),
"Perfect"=> Ok(PlayerDifficulty::Perfect),
"Micky" => Ok(PlayerDifficulty::Micky),
"Elliot" => Ok(PlayerDifficulty::Elliot),
"Cultist"=> Ok(PlayerDifficulty::Cultist),
_ => Err(PlayerError)
}
}
}
And use it with ? to propagate error.
fn main() -> (Result<(),Box<dyn std::error::Error>>) {
let _x = PlayerDifficulty::from_str("Player")?;
let _x = PlayerDifficulty::from_str("PlayerPlayer")?;
Ok(())
}

How to return different tokens during tokenization?

I'm writing a compiler in Rust and, given a string, part of the logic is to find out of which "kind" the characters are.
I want to return the "value" of each character. For an input of 1 + 2 each character has a "token" and should return something like:
NumberToken, 1
WhiteSpaceToken, ' '
PlusToken, '+'
WhiteSpaceToken, ' '
NumberToken, 1
My function should return something like
enum SyntaxKind {
NumberToken,
WhiteSpaceToken,
PlusToken
}
struct SyntaxToken {
kind: SyntaxKind,
value: // Some general type
}
fn next_token(line: String) -> SyntaxToken {
// Logic goes here
}
How would I implement such logic?
If you're writing out a tokenizer and you wonder what such logic might look like, you can couple these values in the same enum, e.g:
#[derive(Debug)]
enum Token {
Add,
Sub,
Whitespace,
Number(f64),
}
For more, see The Rust Programming Language, "Defining an Enum" on adding data to variants.
… and then you can use a match inside of an iterator to handle it accordingly:
#[derive(Debug)]
enum Token {
Add,
Sub,
Whitespace,
Number(f64),
}
use std::str::Chars;
use std::iter::Peekable;
struct Tokens<'a> {
source: Peekable<Chars<'a>>,
}
pub type TokenIterator<'a> = Peekable<Tokens<'a>>;
impl<'a> Tokens<'a> {
pub fn new(s: &'a str) -> TokenIterator {
Self {
source: s.chars().peekable(),
}
.peekable()
}
}
impl<'a> Iterator for Tokens<'a> {
type Item = Token;
fn next(&mut self) -> Option<Self::Item> {
match self.source.next() {
Some(' ') => Some(Token::Whitespace),
Some('+') => Some(Token::Add),
Some('-') => Some(Token::Sub),
n # Some('0'..='9') => {
let mut number = String::from(n.unwrap());
while let Some(n) = self.source.next_if(char::is_ascii_digit) {
number.push(n);
}
Some(Token::Number(number.parse::<f64>().unwrap()))
}
Some(_) => unimplemented!(),
None => None,
}
}
}
fn main() {
let tokens = Tokens::new("1 + 2");
for token in tokens {
println!("{:?}", token);
}
}
This should then give you:
Number(1.0)
Whitespace
Add
Whitespace
Number(2.0)
Playground

How to return when ref and ownership transfer both won't work

so, if I return this
self.string_ref.unwrap().as_ref()
compiler will say
error[E0515]: cannot return value referencing temporary value
returns a value referencing data owned by the current function
if I return this
*self.string_ref.unwrap().as_ref()
the compiler will say
error[E0507]: cannot move out of borrowed content
this is just drove me crazy
here is the code: (playground)
use std::ptr::NonNull;
struct A {
string_ref: Option<NonNull<String>>,
}
struct Number {
num: i32
}
impl A {
fn hello() {
}
fn give_me_string(&self) -> String {
unsafe {
*self.string_ref.unwrap().as_ref()
}
}
}
fn main() {
let a = A {
string_ref: NonNull::new(&mut String::from("hello world") as *mut String)
};
let t = a.give_me_string();
println!("{}", t)
}
Stripping your example to the bare minimum:
struct A {
string_ref: Option<NonNull<String>>,
}
impl A {
fn give_me_string(&self) -> String {
unsafe {
*self.string_ref.unwrap().as_ref()
}
}
}
There are a few errors here:
The most obvious one is that you're trying to take ownership of self.string_ref, even though you've only borrowed self.
To solve this you'll want to use a match statement, which allows you to destructure self.string_ref and not consume it:
fn give_me_string(&self) -> String {
unsafe {
match self.string_ref {
Some(x) => x.as_ref(),
None => panic!("Had no `string_ref`!")
}
}
}
as_ref returns &T, so you can't return an owned string, instead you need to either clone it and then return an owned string, or take reference to it:
//Option one: Clone contents
match self.string_ref {
Some(ref x) => x.as_ref().clone(),
_ => //...
}
//Option two: Return reference.
fn give_me_string(&self) -> &str {
unsafe {
match &self.string_ref {
Some(x) => x.as_ref() as _,
_ => //...
}
}
}
To address another problem mentioned in the comments, you have the following statement in your main function:
string_ref: NonNull::new(&mut String::from("hello world") as *mut String)
This will cause UB due to its nature. You are forming a String by using String::from, but are not storing its value anywhere and are instead immediately casting into a pointer. This will free the String at the end of the line, causing UB.
So I basically figured out what's going on, thanks to #Optimistic Peach
fn give_me_string(&self) -> &String {
unsafe {
match self.string_ref {
Some(x) => &*(x.as_ptr() as *const _), //without ref
Some(ref x) => x.as_ptr(), // with ref
None => panic!("hello?")
}
}
}

How do you unwrap a Result on Ok or return from the function on Err?

I have a function that calls another function which returns a Result. I need to check if the Result is Ok or Err and if it is an Err, I need to return early from my function. This is what I'm doing now:
match callable(&mut param) {
Ok(_v) => (),
Err(_e) => return,
};
Is there a more idiomatic Rust way to do this?
You can create a macro:
macro_rules! unwrap_or_return {
( $e:expr ) => {
match $e {
Ok(x) => x,
Err(_) => return,
}
}
}
fn callable(param: &mut i32) -> Result<i32, ()> {
Ok(*param)
}
fn main() {
let mut param = 0;
let res = unwrap_or_return!(callable(&mut param));
println!("{:?}", res);
}
Note that I wouldn't recommend discarding the errors. Rust's error handling is pretty ergonomic, so I would return the error, even if it is only to log it:
fn callable(param: &mut i32) -> Result<i32, ()> {
Ok(*param)
}
fn run() -> Result<(), ()> {
let mut param = 0;
let res = callable(&mut param)?;
println!("{:?}", res);
Ok(())
}
fn main() {
if let Err(()) = run() {
println!("Oops, something went wrong!");
}
}
If both functions return Result<doesn't matter, same T> you can just put a ? at the end of line of call.
fn caller() -> Result<Str, i32> {
let number = job()?; // <-- if job return error this function return/end here
// otherwise the value of Ok will assign to number
Ok(format!("the number is {}", number))
}
fn job() -> Result<i32, i32> {
// do something
Err(3)
}
You can use same pattern for Option<T> too.
You can use my unwrap_or crate to accomplish this.
You can do:
unwrap_or_ok!(callable(&mut param), _, return);
And if you want the result and to return the error, you can do:
let v = unwrap_or_ok!(callable(&mut param), error, return error);
Rust 1.65.0 has stabilized let-else statements, which enables you to write:
let Ok(_v) = callable(&mut param) else { return };

Resources