I have a large array of statically allocated string slices, defined like so:
const ARR: [&'static str; 50] = [...];
I'm then iterating through the array in what I assume is a normal manner (I'm new to Rust):
for el in ARR.iter() {
if el == target {
return true;
}
}
Unfortunately, I'm getting an error when I try to use eq():
error: the trait `core::cmp::PartialEq<str>` is not implemented for the type `&str`
Is there something in the standard library to compare string slices, or do I have to just iterate through and compare characters myself? And, for that matter, is there a better way to search for an element in an array than what I'm doing?
Thanks!
Here's how you could write your example:
const FRUITS: [&'static str; 3] = ["apple", "banana", "coconut"];
fn is_available(desired: &str) -> bool {
for &el in FRUITS.iter() {
// let () = el; // PROTIP
if el == desired {
return true;
}
}
false
}
See where I assigned el to ()? That's a little trick to see what the type of a variable is at some point. If you uncomment that, you'll get an error like:
error: mismatched types:
expected `&&str`,
found `()`
This lets you know what the types are. The second part is to look at the implementations of PartialEq for str, the important one being:
impl PartialEq<str> for str
So we bind el with a pattern that will automatically dereference it once for us. Then the comparison can take place, as we have a balanced amount of dereferencing to do:
for &el in FRUITS.iter() {
// ^~~ Here
But really, I'd write it like this:
static FRUITS: [&'static str; 3] = ["apple", "banana", "coconut"];
fn main() {
let desired = "apple";
let to_eat = FRUITS.iter().find(|&&f| f == desired);
println!("{:?}", to_eat);
let desired = "durian";
let to_eat = FRUITS.iter().find(|&&f| f == desired);
println!("{:?}", to_eat);
}
static creates an actual shared place in memory for the variable. const acts more like a C #define - the value is inserted everywhere it is used. Since find returns the item, we need it to have some storage that lasts for longer than the one expression.
IteratorExt::find also abstracts the work of finding a matching value (for some condition), and returns an Option denoting success / failure.
Related
I'm working thru some programming exercises on Exercism when I ran into this problem. It's not clear to me why the type of the closure set to for_each() even matters.Here's the entire Rust program:
use std::collections::HashSet;
// reformat(word) returns a tuple mapping its argument to (lowercase, sorted_lowercase)
fn reformat(word: &str) -> (String,String) {
let lower = word.to_lowercase();
let mut char_vec : Vec<char> = lower.chars().collect();
char_vec.sort_unstable();
let sorted : String = char_vec.iter().collect();
(lower,sorted)
}
// Items in 'possible_anagrams' will be added to the set if they contain all of the
// same characters as 'word' but arranged in a different order.
fn anagrams_for<'a>(word: &str, possible_anagrams: &'a [&str]) -> HashSet<&'a str> {
let mut set = HashSet::<&str>::new();
let w = reformat(word);
let is_anagram = |x:&str|->bool{let t=reformat(x);w.1==t.1&&w.0!=t.0};
possible_anagrams.iter().filter(|x|is_anagram(x)).for_each(|x| set.insert(x));
set
}
fn main() {
let a : [&str; 4] = ["FRBA", "Braf", "wut", "batt"];
println!("{:#?}", anagrams_for("Frab", &a));
}
I get an error message about the argument to the for_each() here:
|
18 | possible_anagrams.iter().filter(|x|is_anagram(x)).for_each(|x| set.insert(x));
| ^^^^^^^^^^^^^
expected `()`, found `bool`
The error message is not at all clear to me. I've tried various remedies but any change I make seems to make matters worse, i.e., more error messages.
I have a completely different manifestation of the anagrams_for() function that does work properly. So as far as the coding exercise goes, I have solved it via the following version of this function:
pub fn anagrams_for<'a>(word: &str, possible_anagrams: &'a[&str]) -> HashSet<&'a str> {
let mut set = HashSet::<&str>::new();
let w = reformat(word);
for x in possible_anagrams {
let test = reformat(x);
if w.1 == test.1 && w.0 != test.0 {
set.insert(x);
}
}
set
}
This one functions as I want. I've included it here as an example of what I want the so-far-not-working code to do.
HashSet::insert() returns bool (true if the value was already in the set).
Iterator::for_each() expects its closure to return the unit type (), or "nothing" (void in C).
Rust is expression-oriented: (almost) everything is an expression. Closures (and functions) returns the value of their last expression. That is, || expr is the same as || { return expr; }, and thus your closure returns the return value of insert() - a bool, while for_each() expects it to return ().
The fix is simple: you just need to discard insert()'s return value. It is done by making it into a statement, by appending a semicolon to it. This will also require you to use a block:
possible_anagrams.iter().filter(|x| is_anagram(x)).for_each(|x| { set.insert(x); });
I have provided an example here:
#[derive(Default)]
struct Example {
name: String,
description: String,
}
fn main() {
let lines = vec![
"N: N1",
"D: D1",
"",
"N: N2",
"D: D2",
"",
];
let data = lines.into_iter().fold(Vec::<Example>::new(), |acc, line| {
let mut examples = acc;
match line.chars().collect::<Vec<char>>().as_slice() {
['N', ..] => {
let mut element:Example = Default::default();
element.name = line[2..].into();
examples.push(element);
}
['D', ..] => {
let mut element = examples.pop().unwrap();
element.description = line[2..].into();
examples.push(element);
}
&[_, ..] => {}
&[] => {}
}
return examples;
});
for example in data{
println!("Name: {}, Description: {}", example.name, example.description);
}
}
Playground
Basically, I will be processing a steam of lines (the amount unknown at runtime, I have used an array here for the purpose of the example) and I want to build up a struct with the information and when I reach a given termination point, I start a new struct and add it to the list.
My first attempts at this used an outer most mutable list. I then discovered the fold method which seemed more elegant (IMO) but I still have to make the list mutable inside.
What would be a better way of achieving this and/or how could I remove the need to make the list mutable?
If you always have this same structure (only 2 fields, 3 rows per record), and you can have 2 independent iterators over the data,
it is possible to do a trick:
let names = lines.iter();
let descriptions = lines.iter().skip(1);
let name_desc_pairs = names.zip(descriptions).step_by(3);
let examples = name_desc_pairs.map(parse_example);
Where fn parse_example(lines: (&String, &String)) -> Example would take a pair of (name_line, description_line) strings and construct an Example.
Otherwise if you want arbitrary number of fields, consider that while you iterate over lines, at first you only get a partial example, so some buffering of the partial state is needed. There are no methods for that in the standard Iterator.
There's chunks method in the futures crate if you can use that: stream::iter(lines).chunks(3) spits out vectors of 3 lines, each of which you can parse into an Example.
Without that it's possible to implement your own buffering & parsing Iterator.
The idea is that the iterator state contains a partial example, e.g.:
struct ExampleBuilder {
name: Option<String>,
description: Option<String>,
}
and wraps the original iterator. In its next() method it calls next() of the original iterator a few times, and either adds line data to ExampleBuilder, or when it gets "" separator - converts ExampleBuilder to Example and returns it.
In Kotlin, I can re-use values so:
"127.0.0.1:135".let {
connect(it) ?: System.err.println("Failed to connect to $it")
}
Is anything similar possible in Rust? To avoid using a temporary variable like this:
let text_address = "127.0.0.1:135";
TcpListener::bind(text_address).expect(format!("Failed to connect to {}", text_address));
According to this reference, T.let in Kotlin is a generic method-like function which runs a closure (T) -> R with the given value T passed as the first argument. From this perspective, it resembles a mapping operation from T to R. Under Kotlin's syntax though, it looks like a means of making a scoped variable with additional emphasis.
We could do the exact same thing in Rust, but it doesn't bring anything new to the table, nor makes the code cleaner (using _let because let is a keyword in Rust):
trait LetMap {
fn _let<F, R>(self, mut f: F) -> R
where
Self: Sized,
F: FnMut(Self) -> R,
{
f(self)
}
}
impl<T> LetMap for T {}
// then...
"something"._let(|it| {
println!("it = {}", it);
"good"
});
When dealing with a single value, it is actually more idiomatic to just declare a variable. If you need to constrain the variable (and/or the value's lifetime) to a particular scope, just place it in a block:
let conn = {
let text_address = "127.0.0.1:135";
TcpListener::bind(text_address)?
};
There is also one more situation worth mentioning: Kotlin has an idiom for nullable values where x?.let is used to conditionally perform something when the value isn't null.
val value = ...
value?.let {
... // execute this block if not null
}
In Rust, an Option already provides a similar feature, either through pattern matching or the many available methods with conditional execution: map, map_or_else, unwrap_or_else, and_then, and more.
let value: Option<_> = get_opt();
// 1: pattern matching
if let Some(non_null_value) = value {
// ...
}
// 2: functional methods
let new_opt_value: Option<_> = value.map(|non_null_value| {
"a new value"
}).and_then(some_function_returning_opt);
This is similar
{
let text_address = "127.0.0.1:135";
TcpListener::bind(text_address).expect(format!("Failed to connect to {}", text_address));
}
// now text_address is out of scope
I have a Rust enum defined like this
enum MyFirstEnum {
TupleType(f32, i8, String),
StuctType {varone: i32, vartwo: f64},
NewTypeTuple(i32),
SomeVarName
}
I have the following code:
let mfe: MyFirstEnum = MyFirstEnum::TupleType(3.14, 1, "Hello".to_string());
I'm following the Rust documentation and this looks fine. I don't need to define everything in the enum, but how would I go about accessing the mid element in the enum tuple?
mfe.TupleType.1 and mfe.1 don't work when I add them to a println!
I know Rust provides the facility to do pattern matching to obtain the value, but if I changed the code to define the other variants within the enum, the code to output a particular variant would quickly become a mess.
Is there a simple way to output the variant of the tuple (or any other variant) in the enum?
This is a common misconception: enum variants are not their own types (at least in Rust 1.9). Therefore when you create a variable like this:
let value = MyFirstEnum::TupleType(3.14, 1, "Hello".to_string());
The fact that it's a specific variant is immediately "lost". You will need to pattern match to prevent accessing the enum as the wrong variant. You may prefer to use an if let statement instead of a match:
if let MyFirstEnum::TupleType(f, i, s) = value {
// Values available here
println!("f: {:?}", f);
}
Example solution:
enum MyFirstEnum {
TupleType(f32, i8, String),
// StuctType { varone: i32, vartwo: f64 },
// NewTypeTuple(i32),
// SomeVarName,
}
fn main() {
let mfe: MyFirstEnum = MyFirstEnum::TupleType(3.14, 1, "Hello".to_string());
let MyFirstEnum::TupleType(value, id, text) = &mfe;
println!("[{}; {}; {}]", value, id, text);
//or
match &mfe {
MyFirstEnum::TupleType(value, id, text) => {
println!("[{}; {}; {}]", value, id, text);
}
// _ => {}
}
}
Playground link
I'm having trouble reversing a string in the newest version of rust, I saw this post but I'm still getting some errors. I'm trying to reverse a string and compare the two without any luck:
fn is_palindromic(num: int) -> bool {
let num_str = num.to_str();
let rev_num_str = num_str.chars_rev().collect();
if rev_num_str == num_str {
return true;
}else{
return false;
}
}
then I receive the error:
main.rs:8:28: 8:39 error: type `collections::string::String` does not implement
any method in scope named `chars_rev`
main.rs:8 let rev_num_str = num_str.chars_rev();
Many of the traditionally useful string methods are defined in the std::str::StrSlice trait. To get a slice from a String value, you can call the as_slice method. It looks like chars_rev is no longer defined in the StrSlice trait, but there is a chars method which returns an iterator Chars.
This particular iterator implements the DoubleEndedIterator trait, which means it can be reversed by calling rev. That should be all you need:
fn is_palindromic(num: int) -> bool {
let num_str = num.to_str();
let rev_num_str: String = num_str.as_slice().chars().rev().collect();
return rev_num_str == num_str;
}
fn main() {
println!("{}", is_palindromic(12321));
}
Note that I also added a type annotation to rev_num_str since the compiler can't infer its concrete type (collect is polymorphic and can build many different kinds of "container" values). You could alternatively instantiate collect specifically:
let rev_num_str = num_str.as_slice().chars().rev().collect::<String>();