How can I return the first non-empty line?

The following Python code returns the first non-empty string (content in this example bar

):

foo = ""
bar = "hello"
foo or bar # returns "hello"

      

How do I write it in Rust? I've tried with this:

let foo = "";
let bar = "";
foo || bar;

      

but i get this

error[E0308]: mismatched types
 --> src/main.rs:4:5
  |
4 |     foo || bar;
  |     ^^^ expected bool, found &str
  |
  = note: expected type `bool`
             found type `&str`

      

I suppose I can't easily do what I do in Python with Rust?

+3


source to share


4 answers


Rust has no concept of true or fake values ​​like Python does, so you can't use str

as booleans. You can't actually use anything other than actual bools with comparison operators.

An alternative to @Aratz's solution using match

would be

let result = match (foo.is_empty(), bar.is_empty) {
    (true,_) => Some(foo),
    (_, true) => Some(bar),
    _ => None,
};

      

If you need this look or functionality for multiple lines, you can use a macro:



macro_rules! first_nonempty {
   ( $( $string:expr),+ )=> ({
        $(
            if !$string.is_empty() {
                Some($string)
            } else
        )+
        { None }
   })
}

      

Used like

let res = first_nonempty!("foo", "bar", ""); // res is Some("foo")
let res = first_nonempty!("", "bar", "baz", "quux"); // res is Some("bar")
let res = first_nonempty!(""); // res is None

      

+4


source


If you have multiple lines, I would use iterators;

let strs = ["", "foo", "bar"];
let non_empty = strs.iter().skip_while(|&x| x.is_empty()).next();

println!("{}", non_empty.unwrap_or(&""));

      



It can also come in its own function if you use this a lot:

// call as let non_empty = first_non_empty(&["", "foo", "bar"]);
fn first_non_empty<'a>(strs: &[&'a str]) -> &'a str {
    strs.iter().skip_while(|&x| x.is_empty()).next().unwrap_or(&"")
}

      

+3


source


You can also create an extension tag that will add your desired behavior:

trait Or: Sized {
    fn or(self, other: Self) -> Self;
}

impl<'a> Or for &'a str {
    fn or(self, other: &'a str) -> &'a str {
        if self.is_empty() { other } else { self }
    }
}

      

Now you can use it like this:

assert_eq!("foo".or("bar"), "foo");
assert_eq!("".or("").or("baz").or("").or("quux"), "baz");

      

If you need to make sure the second argument is being evaluated lazily, you can extend the trait Or

like this:

fn or_else<F: FnOnce() -> Self>(self, f: F) -> Self;

      

See similar methods Option

: Option#or

and Option#or_else

for more details.

+3


source


The problem is that Rust won't implicitly convert strings (or whatever) to boolean expressions.

If you want to mimic Python behavior, that is, you want to keep strings after your boolean expression, you have to be more explicit with your code, for example:

if foo != "" {
    foo
} else if bar != "" {
    bar
} else {
    ""
}

      

+1


source







All Articles