I am struggling to understand the principles behind borrowing while creating a simple program. This is what I have
pub struct GameBoard {
p1_icon: String,
p2_icon: String,
current_player: i8,
}
impl GameBoard {
pub fn prompt_turn(&self) {
let misc = String::from("?");
let icon = match self.current_player {
1 => &self.p1_icon,
2 => &self.p2_icon,
_ => &misc,
};
println!(
"It is Player {}'s turn to go. Please pick your next move to drop a {}",
self.current_player, icon
);
println!("This is a test: {}", self.p1_icon)
}
}
My primary question here is, why does Rust require me to borrow with &self.p1_icon within the match call, but not when I call println?
My next question is, I am having challenges working with string literals in Rust. In my case, there are some strings that are meant to be dynamic—thus why I use Strings—but there are others that are just hard-coded (see misc).
It feels a bit overkill to create a String, then borrow from said String, so I can satisfy the constraints that "match values must all be &String". Is this really how Rust expects developers to code this way, or am I missing something?
My primary question here is, why does Rust require me to borrow with &self.p1_icon within the match call, but not when I call println?
println! is a macro, not a regular function call, which allows it to bend the rules. When println! is expanded it inserts hidden & operators to borrow its arguments.
Running cargo expand to expand macros shows that the two println!s expand to:
{
::std::io::_print(
::core::fmt::Arguments::new_v1(
&[
"It is Player ",
"\'s turn to go. Please pick your next move to drop a ",
"\n",
],
&[
::core::fmt::ArgumentV1::new_display(&self.current_player),
// ^-- added borrow
::core::fmt::ArgumentV1::new_display(&icon),
// ^-- added borrow
],
),
);
};
{
::std::io::_print(
::core::fmt::Arguments::new_v1(
&["This is a test: ", "\n"],
&[::core::fmt::ArgumentV1::new_display(&self.p1_icon)],
// ^-- added borrow
),
);
}
Notice how &self.current_player, &icon, and &self.p1_icon are all automatically borrowed.
I don't know if I like how println! does this. It makes it easier to type print statements, but in the process it muddles the borrowing story for new Rustaceans. Rust usually tries to avoid such surprises.
It feels a bit overkill to create a String, then borrow from said String, so I can satisfy the constraints that "match values must all be &String". Is this really how Rust expects developers to code this way, or am I missing something?
You're right, it's overkill. There's no need for misc to be a heap-allocated String. You can make it a plain &str and it'll compile fine.
let misc = "?";
let icon = match self.current_player {
1 => &self.p1_icon,
2 => &self.p2_icon,
_ => misc,
};
If you're wondering why this compiles when the match arms have different types (&String and &str), it's thanks to type coercion (warning: advanced read).
Rust tries to find a common type for the match arms. Using its least upper bound coercion process, the compiler figures out that the result of the match block should be &str, and that it should coerce the two &String arms into &str. Such coercion is possible because of the general rule that coercion is allowed from "&T or &mut T to &U if T implements Deref<Target = U>", which applies since String implements Deref<Target = str>.
In other words, it automatically inserts dereferences as if the match block had been written:
let misc = "?";
let icon = match self.current_player {
1 => &*self.p1_icon,
2 => &*self.p2_icon,
_ => misc,
};
Related
This question already has an answer here:
Returning a reference from a HashMap or Vec causes a borrow to last beyond the scope it's in?
(1 answer)
Closed 3 years ago.
I'm trying to write a function that finds returns a mutable reference to an existing element in a Vec, or inserts it if it doesn't exist and returns a mutable reference to the new element.
I've tried a couple of times, but the borrow checker isn't convinced. I've simplified the code I was trying to write to the example below, which gives the same errors.
fn mut_find_or_insert<T: PartialEq>(vec: &mut Vec<T>, val: T) -> &mut T {
if let Some(u) = vec.iter_mut().find(|u| **u == val) {
u
} else {
vec.push(val);
vec.last_mut().unwrap()
}
}
Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cb12c38bcf3682b15a247d14aab48b6b
Rust gives me the following compiler error (full message through the playground link):
error[E0499]: cannot borrow `*vec` as mutable more than once at a time
This seems to be something that should be possible to implement in rust, however it's not clear to me how I reimplement this to avoid borrow checker errors.
The reason this doesn't work as written is because of a limitation in the current borrow checker. This is very similar to NLL case #3, in which the compiler borrows somewhat overzealously for an entire match statement when the borrow is only used in one of the branches. With the experimental "Polonius" borrow checker (available on the nightly compiler with the -Z polonius flag), your code is accepted as-is.
Working in the stable compiler, it's probably a good idea to redesign your data structures as Sébastien Renauld's answer also suggests, but if you need to make this work with a Vec, you can work around it by briefly using an index to end the borrow:
fn mut_find_or_insert<T: PartialEq>(vec: &mut Vec<T>, val: T) -> &mut T {
if let Some(i) = vec.iter().position(|each| *each == val) {
&mut vec[i]
} else {
vec.push(val);
vec.last_mut().unwrap()
}
}
This works because the result of calling position is not a reference, so the borrow of vec is not held during the if let.
This is similar to the following questions, which manage to find the same limitation using early return from a loop:
Double mutable borrow error in a loop happens even with NLL on
"Variable does not live long enough" when returning a Result containing a reference but it does live long enough
Vec is an unordered, not very structured type. It has no way to look up the exact position of an item within it; the closest the default functions get to is contains(), which only tells you if the item is contained.
Furthermore, due to the fact that a Vec is not a Set, the behavior of "find the item or append and return " is undefined - "find the item", if there is a duplicate, needs to be defined further.
To solve this problem without changing to the correct type (HashSet is the type you really want for this. Note the existence of get_or_insert(), which is literally what you are after. It pays to use the proper structure for the job, rather than to try to make everything fit a Vec), we're going to have to build it ourselves. Keeping to your signature, it looks like this (Playground):
trait VecSeekOrAppend<T:PartialEq>:Sized {
fn get_or_insert(&mut self, item: T) -> &mut T;
}
impl<T> VecSeekOrAppend<T> for Vec<T>
where T: PartialEq + Clone {
fn get_or_insert(&mut self, item: T) -> &mut T {
if !self.contains(&item) {
self.push(item.clone());
}
for i in self.iter_mut() {
if i == &mut item {
return i;
}
}
unreachable!();
}
}
The reason your initial version does not work is due to the returned lifetime requirement; all methods returning a reference from a Vec require a lifetime validity for the duration of use. By returning such a &mut reference, if you attempt to do it in one go, the mutation of the Vec<_> will happen while there is already a mutable borrow of it.
Splitting the cycle in two, and performing the insertion (without keeping a reference) to then find the eference, allows us to sidestep this problem. Another way to perform this is to store items by a serializable or hashable identifier (the exact way HashMap and HashSet work) in order to innately provide this layer of indirection.
There is a rust feature in the works to ease some of this pain (non-lexical lifetimes), but, as you can see from the github issue, it's not in the near future.
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);
}
This question already has an answer here:
Returning a reference from a HashMap or Vec causes a borrow to last beyond the scope it's in?
(1 answer)
Closed 3 years ago.
I'm trying to write a function that finds returns a mutable reference to an existing element in a Vec, or inserts it if it doesn't exist and returns a mutable reference to the new element.
I've tried a couple of times, but the borrow checker isn't convinced. I've simplified the code I was trying to write to the example below, which gives the same errors.
fn mut_find_or_insert<T: PartialEq>(vec: &mut Vec<T>, val: T) -> &mut T {
if let Some(u) = vec.iter_mut().find(|u| **u == val) {
u
} else {
vec.push(val);
vec.last_mut().unwrap()
}
}
Playground link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=cb12c38bcf3682b15a247d14aab48b6b
Rust gives me the following compiler error (full message through the playground link):
error[E0499]: cannot borrow `*vec` as mutable more than once at a time
This seems to be something that should be possible to implement in rust, however it's not clear to me how I reimplement this to avoid borrow checker errors.
The reason this doesn't work as written is because of a limitation in the current borrow checker. This is very similar to NLL case #3, in which the compiler borrows somewhat overzealously for an entire match statement when the borrow is only used in one of the branches. With the experimental "Polonius" borrow checker (available on the nightly compiler with the -Z polonius flag), your code is accepted as-is.
Working in the stable compiler, it's probably a good idea to redesign your data structures as Sébastien Renauld's answer also suggests, but if you need to make this work with a Vec, you can work around it by briefly using an index to end the borrow:
fn mut_find_or_insert<T: PartialEq>(vec: &mut Vec<T>, val: T) -> &mut T {
if let Some(i) = vec.iter().position(|each| *each == val) {
&mut vec[i]
} else {
vec.push(val);
vec.last_mut().unwrap()
}
}
This works because the result of calling position is not a reference, so the borrow of vec is not held during the if let.
This is similar to the following questions, which manage to find the same limitation using early return from a loop:
Double mutable borrow error in a loop happens even with NLL on
"Variable does not live long enough" when returning a Result containing a reference but it does live long enough
Vec is an unordered, not very structured type. It has no way to look up the exact position of an item within it; the closest the default functions get to is contains(), which only tells you if the item is contained.
Furthermore, due to the fact that a Vec is not a Set, the behavior of "find the item or append and return " is undefined - "find the item", if there is a duplicate, needs to be defined further.
To solve this problem without changing to the correct type (HashSet is the type you really want for this. Note the existence of get_or_insert(), which is literally what you are after. It pays to use the proper structure for the job, rather than to try to make everything fit a Vec), we're going to have to build it ourselves. Keeping to your signature, it looks like this (Playground):
trait VecSeekOrAppend<T:PartialEq>:Sized {
fn get_or_insert(&mut self, item: T) -> &mut T;
}
impl<T> VecSeekOrAppend<T> for Vec<T>
where T: PartialEq + Clone {
fn get_or_insert(&mut self, item: T) -> &mut T {
if !self.contains(&item) {
self.push(item.clone());
}
for i in self.iter_mut() {
if i == &mut item {
return i;
}
}
unreachable!();
}
}
The reason your initial version does not work is due to the returned lifetime requirement; all methods returning a reference from a Vec require a lifetime validity for the duration of use. By returning such a &mut reference, if you attempt to do it in one go, the mutation of the Vec<_> will happen while there is already a mutable borrow of it.
Splitting the cycle in two, and performing the insertion (without keeping a reference) to then find the eference, allows us to sidestep this problem. Another way to perform this is to store items by a serializable or hashable identifier (the exact way HashMap and HashSet work) in order to innately provide this layer of indirection.
There is a rust feature in the works to ease some of this pain (non-lexical lifetimes), but, as you can see from the github issue, it's not in the near future.
I'm trying to learn Rust's lifetime rules by comparing it to similar concepts in C++, which I'm more familiar with. Most of the time, my intuition works really well and I can make sense the rule. However, in the following case, I'm not sure if my understanding is correct or not.
In Rust, a temporary value's lifetime is the end of its statement, except when the last temporary value is bound to a name using let.
struct A(u8);
struct B(u8);
impl A {
fn get_b(&mut self) -> Option<B> {
Some(B(self.0))
}
}
fn a(v: u8) -> A {
A(v)
}
// temporary A's lifetime is the end of the statement
// temporary B binds to a name so lives until the enclosing block
let b = a(1).get_b();
// temporary A's lifetime is the end of the statement
// temporary B's lifetime extends to the enclosing block,
// so that taking reference of temporary works similar as above
let b = &a(2).get_b();
If the temporary value is in an if condition, according to the reference, the lifetime is instead limited to the conditional expression.
// Both temporary A and temporary B drops before printing some
if a(3).get_b().unwrap().val <= 3 {
println!("some");
}
Now to the question:
If putting let in if condition, because of pattern matching, we are binding to the inner part of the temporary value. I'd expect the temporary value bound by let to be extended to the enclosing block, while other temporary values should still have a lifetime limited by the if condition.
(In this case actually everything is copied I would say even temporary B can be dropped, but that's a separate question.)
However, both temporaries' lifetimes are extended to the enclosing if block.
// Both temporary A and temporary B's lifetime are extended to the end of the enclosing block,
// which is the if statement
if let Some(B(v # 0...4)) = a(4).get_b() {
println!("some {}", v);
}
Should this be considered an inconsistency in Rust? Or am I misunderstanding and there is a consistent rule that can explain this behavior?
Full code example:
playground
The same thing implemented in C++ that matches my expectation
Note the output from Rust is
some 4
Drop B 4
Drop A 4
while the output from C++ is
Drop A 4
some 4
Drop B 4
I have read this Reddit thread and Rust issue, which I think is quite relevant, but I still can't find a clear set of lifetime rule that works for all the cases in Rust.
Update:
What I'm unclear about is why the temporary lifetime rule about if conditional expression does not apply to if let. I think the let Some(B(v # 0...4)) = a(4).get_b() should be the conditional expression, and thus the temporary A's lifetime should be limited by that, rather than the entire if statement.
The behaviour of extending temporary B's lifetime to the entire if statement is expected, because that is borrowed by the pattern matching.
An if let construct is just syntactic sugar for a match construct. let Some(B(v # 0...4)) = a(4).get_b() is not a conditional used in a regular if expression, because it is not an expression that evaluates to bool. Given your example:
if let Some(B(v # 0...4)) = a(4).get_b() {
println!("some {}", v);
}
It will behave exactly the same as the below example. No exceptions. if let is rewritten into match before the type or borrow checkers are even run.
match a(4).get_b() {
Some(B(v # 0...4)) => {
println!("some {}", v);
}
_ => {}
}
Temporaries live as long as they do in match blocks because they sometimes come in handy. Like if your last function was fn get_b(&mut self) -> Option<&B>, and if the temporary didn't live for the entire match block, then it wouldn't pass borrowck.
If conditionals don't follow the same rule because it's impossible for the last function call in an if conditional to hold a reference to anything. They have to evaluate to a plain bool.
See:
Rust issue 37612
I'm trying to implement the following code, which removes the prefix from a slice of Cow<str>'s.
fn remove_prefix(v: &mut [Cow<str>], prefix: &str) {
for t in v.iter_mut() {
match *t {
Borrowed(&s) => s = s.trim_left_matches(prefix),
Owned(s) => s = s.trim_left_matches(prefix).to_string(),
}
}
}
I have two questions:
I can't get this to compile - I've tried loads of combinations of &'s and *'s but to no avail.
Is there a better way to apply functions to a Cow<str> without having to match it to Borrowed and Owned every time. I mean it seems like I should just be able to do something like *t = t.trim_left_matches(prefix) and if t is a Borrowed(str) it leaves it as a str (since trim_left_matches allows that), and if it is an Owned(String) it leaves it as a String. Similarly for replace() it would realise it has to convert both to a String (since you can't use replace() on a str). Is something like that possible?
Question #1 strongly implies how you think pattern matching and/or pointers work in Rust doesn't quite line up with how they actually work. The following code compiles:
fn remove_prefix(v: &mut [Cow<str>], prefix: &str) {
use std::borrow::Cow::*;
for t in v.iter_mut() {
match *t {
Borrowed(ref mut s) => *s = s.trim_left_matches(prefix),
Owned(ref mut s) => *s = s.trim_left_matches(prefix).to_string(),
}
}
}
If your case, Borrowed(&s) is matched against Borrowed(&str), meaning that s is of type str. This is impossible: you absolutely cannot have a variable of a dynamically sized type. It's also counter-productive. Given that you want to modify s, binding to it by value won't help at all.
What you want is to modify the thing contained in the Borrowed variant. This means you want a mutable pointer to that storage location. Hence, Borrowed(ref mut s): this is not destructuring the value inside the Borrowed at all. Rather, it binds directly to the &str, meaning that s is of type &mut &str; a mutable pointer to a (pointer to a str). In other words: a mutable pointer to a string slice.
At that point, mutating the contents of the Borrowed is done by re-assigning the value through the mutable pointer: *s = ....
Finally, the exact same reasoning applies to the Owned case: you were trying to bind by-value, then mutate it, which cannot possibly do what you want. Instead, bind by mutable pointer to the storage location, then re-assign it.
As for question #2... not really. That would imply some kind of overloading, which Rust doesn't do (by deliberate choice). If you are doing this a lot, you could write an extension trait that adds methods of interest to Cow.
You can definitely do it.
fn remove_prefix(v: &mut [Cow<str>], prefix: &str) {
for t in v.iter_mut() {
match *t {
Cow::Borrowed(ref mut s) => *s = s.trim_left_matches(prefix),
Cow::Owned(ref mut s) => *s = s.trim_left_matches(prefix).to_string(),
}
}
}
ref mut s means “take a mutable reference to the value and call it s” in a pattern. Thus you have s of type &mut &str or &mut String. You must then use *s = in order to change what that mutable reference is pointing to (thus, change the string inside the Cow).