My situation:
let mut str_vect = Vec::new();
str_vect.push("a");
str_vect.push("b");
str_vect.push("c");
let [a, bc # ..] = str_vect[..];
println!("{:?} {:?}", a, bc);
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8e4c47b024e0c871676626069ec07d52
I get this error:
the size for values of type [&str] cannot be known at compilation time
Why is that? Can I somehow destructure of Vec of &str?
Just add a &: v[..] is a DST ([T], not &[T]). This is fine as long as we only access individual elements, but for rest patterns (..), that produce a slice - they will have to produce [T], too, and that is unsized. If you match against &[T], on the other hand, the rest pattern also gives &[T] (another possibility is to use ref - ref bc # .., but I would not recommend that).
let [a, bc # ..] = &str_vect[..];
Playground.
However, this fails with:
error[E0005]: refutable pattern in local binding: `&[]` not covered
--> src/main.rs:6:8
|
6 | let [a, bc # ..] = &str_vect[..];
| ^^^^^^^^^^^^ pattern `&[]` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `&[&str]`
help: you might want to use `if let` to ignore the variant that isn't matched
|
6 | let (a, bc) = if let [a, bc # ..] = &str_vect[..] { (a, bc) } else { todo!() };
| ++++++++++++++++ ++++++++++++++++++++++++++++
That is because any slice pattern except [..] is refutable - that is, may fail. What if str_vect (theoretically) has zero elements? We cannot bind the first element in that case! You need if let or match to handle that case.
Another solution, which is definitely not destructuring, but which is easier to read in my opinion because the name expresses the intention, would rely on split_first().
let (a, bc) = str_vect.split_first().unwrap();
Of course, some appropriate error handling should be considered instead of unwrap.
And yes, the other answer is far better because it explains in details the origin of the reported error (I just upvoted it ;^).
Another option would be to use iterators:
fn main() {
let mut str_vect = Vec::new();
str_vect.push("a");
str_vect.push("b");
str_vect.push("c");
let (a, bc) = {
let mut it = str_vect.into_iter();
(it.next(), it)
};
println!("{:?} {:?}", a.unwrap(), bc.collect::<Vec<_>>());
}
Playground
Of course this makes you have to check and/or collect to whatever you need later on. You could just use the iterator if you need. Notice I used into_iter which consumes the vector, but you could use iter to just work with references instead too.
Related
Here is the exercise on the exercism
I just wanted to learn functional way.
use std::collections::HashMap;
pub fn can_construct_note(magazine: &[&str], note: &[&str]) -> bool {
let mut words: HashMap<&str, i32> = HashMap::new();
magazine.iter().map(|&w|
words.entry(w)
.and_modify(|e| *e += 1)
.or_insert(1)
);
println!("{:?}", words);
false
}
But I got this weird error and googled but I can't solve.
I understand that it can't be done by this way.
I want to know the correct way to do this.
Thanks.
error: captured variable cannot escape `FnMut` closure body
--> src/lib.rs:11:9
|
8 | let mut words: HashMap<&str, i32> = HashMap::new();
| --------- variable defined here
9 |
10 | let mut t = magazine.iter().map(|&w|
| - inferred to be a `FnMut` closure
11 | words.entry(w)
| ^----
| |
| _________variable captured here
| |
12 | | .and_modify(|e| *e += 1)
13 | | .or_insert(1)
| |_________________________^ returns a reference to a captured variable which escapes the closure body
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
error: aborting due to previous error
error: could not compile `magazine_cutout`
To learn more, run the command again with --verbose.
One problem with your code is that Iterator::map() is lazy and converts the iterator to another iterator without actually iterating over either. Because of that ending the expression with map() is not very useful, you need to do something that will exhaust the iterator. If you want to do it the functional way, you probably want for_each().
The other problem is that Entry::or_insert() returns a mutable reference to the inserted/retrieved value. This is normally used to chain operations that modify the value, such as or_insert(vec![]).push(item). Your closure doesn't end with ;, so its return value is the reference returned by or_insert(). Rust interprets such map() invocation as intending to transform the iterator over words to an iterator over mutable references to their counts. You would then be free to do whatever you want with those references, perhaps collect them in a vector. This is of course a big problem, as you're not allowed to have more than one mutable reference to anything inside the hashmap at once. This is why the borrow checker complains of the reference leaking out of the closure. To fix this, just add the braces and use a ;, so the closure returns () (which is incidentally the only valid return type of for_each()).
This compiles:
use std::collections::HashMap;
pub fn can_construct_note(magazine: &[&str], note: &[&str]) -> bool {
let mut words: HashMap<&str, i32> = HashMap::new();
magazine.iter().for_each(|&w| {
words.entry(w).and_modify(|e| *e += 1).or_insert(1);
});
println!("{:?}", words);
false
}
Playground
As others pointed out in the comments, an even more functional approach would be to use Iterator::fold(), which wouldn't require a mutating capture of the hashmap.
Functional programming is a way of approaching problems. It's not about syntax. Using map to modify an external mutable HashMap isn't functional programming. It's just an abuse of map (mapping should have no side-effects). There is nothing functional at all about for_each. It's just another syntax for for...in (and IMO an inferior syntax in most cases).
Broadly, functional programming avoids mutation, and thinking about problems recursively rather than by looping. Ömer Erden's solution is good in that it encapsulates the mutation inside the fold, but it's still basically a fancy loop. There's not a lot "functional" about that.
A functional way to think about this problem is recursively. Sort the words in both lists. Then the core mechanic is: Look at the first word in each list. If they match, recurse on the next word in each list. If they don't, recurse on the same goal list, and the next word on the source list. If the goal list is empty: success. If the source list is empty: fail. Notice that there was never a "count" step in there, and there's no HashMap. So I'm skipping your direct question and focusing on solving the full problem (since you said you wanted to explore functional approaches).
The first step towards that is to sort the words. There's no non-mutating sorted method in std, but there is one in IterTools. Still, I can make a simple (and extremely sloppy and special-case) one.
fn sorted<'a>(items: &[&'a str]) -> Vec<&'a str> {
let mut v = Vec::from(items);
v.sort();
v
}
Note that this function is not internally "functional." But it provides a functional interface. This is very common in FP. Functional approaches are sometimes slower than imperative/mutating approaches. We can keep the value of FP while improving performance by encapsulating mutation.
But with that in place, I can build a fully functional solution. Notice the lack of any mut.
pub fn can_construct_note(magazine: &[&str], note: &[&str]) -> bool {
// source and goal are sorted
fn f(source: &[&str], goal: &[&str]) -> bool {
// Split the source and goal into their heads and tails
// Consider just the first elements.
match (source.split_first(), goal.split_first()) {
(_, None) => true, // Can make nothing from anything
(None, Some(_)) => false, // Can't make something from nothing
(Some((s, s_rest)), Some((g, g_rest))) => {
match s.cmp(g) {
// If they match, then recurse on the tails
Ordering::Equal => f(s_rest, g_rest),
// If source < goal, they may match eventually, recurse with the next source element
Ordering::Less => f(s_rest, goal),
// If source > goal, it'll never work
Ordering::Greater => false,
}
}
}
}
// Sort the initial lists
let source = sorted(magazine);
let goal = sorted(note);
// And kick it off and return its result
f(&source[..], &goal[..])
}
This is a very functional way to solve the problem, to the point of being a text-book example. But notice there's not a single map, reduce, fold, or filter anywhere. Those are really important tools in functional programming, but they're not what it means to be functional.
It's not really great Rust. If these lists are very long, then this will likely crash the stack because Rust does not have tail-call optimization (which is a critical optimization for recursion to be really workable).
Recursion can always be turned into a loop, however. So at the cost of a small amount of visible mutation, this can be rewritten. Rather than recursively calling f(...), this changes source and goal and loops.
pub fn can_construct_note(magazine: &[&str], note: &[&str]) -> bool {
let mut source = &sorted(magazine)[..];
let mut goal = &sorted(note)[..];
// source and goal are sorted
loop {
// Split the source and goal into their heads and tails
match (source.split_first(), goal.split_first()) {
(_, None) => return true, // Can make nothing from anything
(None, Some(_)) => return false, // Can't make something from nothing
(Some((s, s_rest)), Some((g, g_rest))) => {
match s.cmp(g) {
// If they match, then recurse on the tails
Ordering::Equal => {
source = s_rest;
goal = g_rest;
continue;
}
// If source < goal, they may match eventually, recurse with the next source element
Ordering::Less => {
source = s_rest;
continue;
}
// If source > goal, it'll never work
Ordering::Greater => return false,
}
}
}
}
}
To Ömer's comments below, this is how you would create the HashMap itself in a functional way. This requires +nightly for the GroupBy.
#![feature(slice_group_by)]
use std::iter::FromIterator;
fn word_count<'a>(strings: &[&'a str]) -> HashMap<&'a str, usize> {
let sorted_strings = sorted(strings);
let groups = sorted_strings
.group_by(|a, b| a == b)
.map(|g| (g[0], g.len()));
HashMap::from_iter(groups)
}
I'm not worried about careful lifetime management here. I'm just focused on how to think in FP. This approach sorts the strings, then groups the strings by equality, and then maps those groups into a tuple of "the string" and "the number of copies." That list of tuples is then turned into a HashMap. There's no need for any mutable variables.
If you want really functional way you should do this:
use std::collections::HashMap;
fn main() {
let s = "aasasdasdasdasdasdasdasdfesrewr";
let map = s.chars().fold(HashMap::new(), |mut acc, c| {
acc.entry(c).and_modify(|x| *x += 1).or_insert(1i32);
acc
});
println!("{:?}", map);
}
i have a simple HashMap; say HashMap<char, char>.
is there a way to swap two elements in this hashmap using std::mem::swap (or any other method)?
Of course there is the simple way getting the values with get and then replace them with insert - but that would trigger the hasher twice (once for getting then for inserting) and i was looking for a way to side-step the second hasher invocation (more out of curiosity than for performance).
what i tried is this (in several versions; none of which worked - and as remarked in the comments: entry would not do what i expect even if i got this past the compiler):
use std::collections::HashMap;
use std::mem::swap;
let mut hash_map: HashMap<char, char> = HashMap::default();
hash_map.insert('A', 'Z');
hash_map.insert('B', 'Y');
swap(&mut hash_map.entry('A'), &mut hash_map.entry('B'));
now the compiler complains (an i understand why it should)
error[E0499]: cannot borrow `hash_map` as mutable more than once at a time
--> tests.rs:103:42
|
103 | swap(&mut hash_map.entry('A'), &mut hash_map.entry('B'));
| ---- -------- ^^^^^^^^ second mutable borrow occurs here
| | |
| | first mutable borrow occurs here
| first borrow later used by call
also just getting the two values this way fails in more or less the same way:
let mut a_val = hash_map.get_mut(&'A').expect("failed to get A value");
let mut b_val = hash_map.get_mut(&'B').expect("failed to get B value");
swap(&mut a_val, &mut b_val);
is there a way to simply swap two entries of a HashMap?
I can't see any safe way to do it:
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert('A', 'Z');
map.insert('B', 'Y');
let a = map.get_mut(&'A').unwrap() as *mut char;
let b = map.get_mut(&'B').unwrap() as *mut char;
unsafe {
std::ptr::swap(a, b);
}
assert_eq!(map.get(&'A'), Some(&'Y'));
assert_eq!(map.get(&'B'), Some(&'Z'));
}
There is one completely safe way I can think of to do this safely, but it's super inefficient: what you want is to get two &mut values, which means borrowck needs to know they're nonoverlapping. Missing a builtin along the lines of split_mut (or the collection being handled specially), the only way I see is to mutably iterate the entire collection, keep refs to the items you're interested in, and swap that:
let mut h = HashMap::new();
h.insert("a", "a");
h.insert("b", "b");
let mut it = h.iter_mut();
let e0 = it.next().unwrap();
let e1 = it.next().unwrap();
std::mem::swap(e0.1, e1.1);
println!("{:?}", h);
It requires a linear traversal of the map until you've found the entries whose values you want to swap though. So even though this has the advantage of not hashing at all edwardw's is answer is probably more practical.
I am playing around with Rust references:
fn main() {
let str = String::from("Hallo");
let &x = &str;
}
This produces the following error:
error[E0507]: cannot move out of borrowed content
--> src/main.rs:3:9
|
3 | let &x = &str;
| ^-
| ||
| |hint: to prevent move, use `ref x` or `ref mut x`
| cannot move out of borrowed content
What is going on here?
Adding to wiomoc's answer: depending on what language(s) you've previously known, variable declaration in Rust might be a little different. Whereas in C/C++ you explicitly have to declare that you want a pointer/reference variable:
int *p = &other_int;
In Rust it's enough to just use let, so the above would in Rust be
let p = &other_int;
and when you write
let &s = &string;
It pattern-matches that, so the Rust compiler reads it roughly as "I know I have a reference, and I want to bind whatever it is referring to to the name p". If you're not familiar with pattern-matching, a more obvious example (that works in Rust as well) would be
let point = (23, 42);
let (x, y) = point;
The second line unpacks the right-hand side to match the left-hand side (both are tuples of two values) and binds the variable names on the left to the values at the same position in the structure on the right. In the case above, it's less obvious that it's matching your "structural description".
The result of let &x = &str;, i.e. "I know &str is a reference, please bind whatever it refers to to the variable x" means that you're trying to have x be the same as str, when at that line all you have is a borrowed reference to str. That's why the compiler tells you you can't create an owned value (which x would be, because it's not being created as a reference) from a reference.
You dont need that &at let x
let str = String::from("Hallo");
let x = &str;
Or if you want to declare the type manually
let string = String::from("Hallo");
let x: &str = &string;
I tried to use a String vector inside another vector:
let example: Vec<Vec<String>> = Vec::new();
for _number in 1..10 {
let mut temp: Vec<String> = Vec::new();
example.push(temp);
}
I should have 10 empty String vectors inside my vector, but:
example.get(0).push(String::from("test"));
fails with
error[E0599]: no method named `push` found for type `std::option::Option<&std::vec::Vec<std::string::String>>` in the current scope
--> src/main.rs:9:20
|
9 | example.get(0).push(String::from("test"));
| ^^^^
Why does it fail? Is it even possible to have an vector "inception"?
I highly recommend reading the documentation of types and methods before you use them. At the very least, look at the function's signature. For slice::get:
pub fn get<I>(&self, index: I) -> Option<&<I as SliceIndex<[T]>>::Output>
where
I: SliceIndex<[T]>,
While there's some generics happening here, the important part is that the return type is an Option. An Option<Vec> is not a Vec.
Refer back to The Rust Programming Language's chapter on enums for more information about enums, including Option and Result. If you wish to continue using the semantics of get, you will need to:
Switch to get_mut as you want to mutate the inner vector.
Make example mutable.
Handle the case where the indexed value is missing. Here I use an if let.
let mut example: Vec<_> = std::iter::repeat_with(Vec::new).take(10).collect();
if let Some(v) = example.get_mut(0) {
v.push(String::from("test"));
}
If you want to kill the program if the value is not present at the index, the shortest thing is to use the index syntax []:
example[0].push(String::from("test"));
I have a string from a CSV file that contains multiple lines:
Edit. Rust was not in the actual string.
header1,header2,header3
r1v1,r1v2,r1v3
r2v1,r2v2,r2v3
I am trying to push these into a Vec<Vec<String>>:
// the csv is raw_string
let lines = raw_string.lines();
let mut mainVec = Vec::new();
for row in lines {
let mut childVec = Vec::new();
let trimmed = row.trim();
let values = trimmed.split(',').collect();
childVec.push(values);
mainVec.push(childVec.clone());
}
But I get the error
error[E0282]: unable to infer enough type information about `_`
--> src/main.rs:9:13
|
9 | let values = trimmed.split(',').collect();
| ^^^^^^ cannot infer type for `_`
|
= note: type annotations or generic parameter binding required
Another solution, based on iterators:
fn main() {
let raw_string = r"rust
header1,header2,header3
r1v1,r1v2,r1v3
r2v1,r2v2,r2v3";
let main_vec = raw_string.lines()
.map(|s| s.trim().split(',').map(String::from).collect::<Vec<String>>())
.collect::<Vec<Vec<String>>>();
print!("{:?}", main_vec);
}
As #Simon Whitehead has already said, the the only thing you need to do with collect() is to specify the type because this is a generic method. But it may also be deduced by the compiler itself in some circumstances.
The code above is pretty verbose about type specification: actually you may leave the Vec's value type unspecified and let it be deduced by the compiler like this:
let main_vec = raw_string.lines()
.map(|s| s.trim().split(',').map(String::from).collect::<Vec<_>>())
.collect::<Vec<_>>();
For more information, see the definition of collect() method.
You need to give the compiler a little hint as to what you want values to be.
You can say you want it to be a vector of something:
let values: Vec<_> = trimmed.split(',').collect();
Working example in the playground
A few other notes about the code, if you're interested.
The variable names should be snake_case. So the vectors should be called main_vec and child_vec.
The child_vec does not need to be cloned when pushing it to the main_vec. Pushing it to main_vec transfers ownership and the loop redeclares the child_vec and so the compiler can guarantee nothing has been violated here.