Fold with string array - string

I tried some code like this:
fn main() {
let a = vec!["May", "June"];
let s = a.iter().fold("", |s2, s3|
s2 + s3
);
println!("{}", s == "MayJune");
}
Result:
error[E0369]: cannot add `&&str` to `&str`
--> a.rs:4:10
|
4 | s2 + s3
| -- ^ -- &&str
| | |
| | `+` cannot be used to concatenate two `&str` strings
| &str
|
help: `to_owned()` can be used to create an owned `String` from a string
reference. String concatenation appends the string on the right to the string
on the left and may require reallocation. This requires ownership of the string
on the left
|
4 | s2.to_owned() + s3
| ^^^^^^^^^^^^^
Ok, fair enough. So I change my code to exactly that. But then I get this:
error[E0308]: mismatched types
--> a.rs:4:7
|
4 | s2.to_owned() + s3
| ^^^^^^^^^^^^^^^^^^
| |
| expected `&str`, found struct `std::string::String`
| help: consider borrowing here: `&(s2.to_owned() + s3)`
Ok, fair enough. So I change my code to exactly that. But then I get this:
error[E0515]: cannot return reference to temporary value
--> a.rs:4:7
|
4 | &(s2.to_owned() + s3)
| ^--------------------
| ||
| |temporary value created here
| returns a reference to data owned by the current function
Why is Rust giving bogus suggestion, and is what I am trying to do possible? Note, I would prefer to avoid suggestions such as "just use join" or similar, as this question is intended to address a more generic problem. Rust version:
rustc 1.46.0 (04488afe3 2020-08-24)

is what I am trying to do possible?
Stargazeur provided a working version in their comment: the initial value / accumulator needs to be a String rather than an &str.
Why is Rust giving bogus suggestion
Rustc doesn't have a global-enough vision so it is able to see the "detail" issue but it doesn't realise that it's really a local effect of a larger problem: fold's signature is
fn fold<B, F>(self, init: B, f: F) -> B
because you're giving fold an &str, it must ultimately return an &str, which is only possible if F just returns something it gets from "the outside", not if it creates anything internally. Since you want to create something inside your callback, the value of init is the issue.
Rustc doesn't see the conflict at that level though, because as far as it's concerned that's a perfectly valid signature e.g. you might be following a chain of things through a hashmap of returning a constant string reference for all it cares, the only real conflict it sees is between this:
F: FnMut(B, Self::Item) -> B
and the implementation of your function which doesn't actually work, so it tries to help you with that:
Rust doesn't allow adding two &str together because that would implicitly allocate a String which is the sort of hidden concern the core team would rather not hide, so Add is only implemented between String and &str, that's the first issue you see, and since that's somewhat unusual (the average language just lets you concatenate string-ish stuff or even not-at-all-strings to strings) rustc devs have added a help text noting that the LHS must be an owned String, which generally helps / works but
then the addition returns a String, so now your function doesn't match the F signature anymore: since the init is an &str that's the type of the accumlator so you need to return an &str
except if you try to create a reference to the string you've just created, you just created it inside the function, once the function returns the string will be dead and the reference left dangling, which rust can not allow
And that's how despite the best intentions, because the compiler's view is too local it guilelessly leads you down a completely useless path of frustration.
You may want to report this issue on the bug tracker (or see if it's already there). I don't know if the compiler diagnostics system would be able to grok this situation though.

Related

Rust mapping wants me to put a block

When working on a training problem for rust, I needed to take all items in a vector, square each of them, and then sum them. I realize that this is not good code and that changing it is faster than asking StackOverflow. I will be changing how this works but right now I'm just trying to learn how to use map and no examples seem to help me with this problem. This is for understanding, but if you have a more idiomatic way to code this quite simply, I would also love to see that. Here is the line of code:
let thing1 = divs.into_iter().map(|n| -> n*n).collect::<Vec<u64>>.iter().sum();
The important bit being:
divs.into_iter().map(|n| -> n*n)
Here is the error:
error: expected `{`, found `*`
--> src/lib.rs:10:51
|
10 | let thing1 = divs.into_iter().map(|n| -> n*n).collect::<Vec<u64>>.iter().sum();
| ^ expected `{`
|
help: try placing this code inside a block
|
10 | let thing1 = divs.into_iter().map(|n| -> n{ *n }).collect::<Vec<u64>>.iter().sum();
| + +
error: could not compile `challenge` due to previous error
This error persists regardless of what operation I do on n, n+1, etc. I tried doing what the complier wanted and it thought I was trying to dereference n. I don't understand why map would act this way - all examples I've seen don't use blocks in map.
You would only want to put -> for a closure to denote the return type. n*n is not a type, so the compiler tries to guess that you meant n as the return type and *n as the closure body, which could be valid syntax if the braces are added.
Simply remove the ->.

Why the order of computing operands is direct and not reverse? (Swapping in a Vec without a local variable) [duplicate]

This question already has an answer here:
Why is indexing a mutable vector based on its len() considered simultaneous borrowing?
(1 answer)
Closed 2 months ago.
This post was edited and submitted for review last month and failed to reopen the post:
Original close reason(s) were not resolved
All I wanna do is to swap the first and the last element in Vec.
So I wrote this:
// getting a vector of integers from stdin:
let mut ranks = std::io::stdin()
.lock()
.lines()
.next()
.unwrap()
.unwrap()
.trim()
.split_whitespace()
.map(|s| s.parse::<usize>().unwrap())
.collect::<Vec<usize>>();
// ...
// pushing something to ranks
// ...
ranks.swap(0, ranks.len() - 1); // <------ TRYING TO SWAP
And of course, it doesn't work, because
error[E0502]: cannot borrow `ranks` as immutable because it is also borrowed as mutable
--> src\main.rs:4:19
|
4 | ranks.swap(0, ranks.len() - 1);
| --------------^^^^^^^^^^^-----
| | | |
| | | immutable borrow occurs here
| | mutable borrow later used by call
| mutable borrow occurs here
|
help: try adding a local storing this argument...
--> src\main.rs:4:19
|
4 | ranks.swap(0, ranks.len() - 1);
| ^^^^^^^^^^^
help: ...and then using that local as the argument to this call
By using this piece of advice the compiler gave me, I came up with following:
let last = ranks.len() - 1;
ranks.swap(0, last);
...which looks terrible.
So the thing is: why do I need to create local variable to store vector length, if I want to pass it to the method? Well, of course because of the borrowing rules: the value of ranks will be borrowed as mutable as I call swap, so that I can't use ranks.len() anymore.
But isn't it reasonable to suppose that the values of parameters will be calculated before the method starts to do anything and before it somehow can change the content of vector or it's length?
Basically, the order of computing parameters of a function is straight in Rust, which creates many obsticles for writing clean code for me. So I would like to ask, why the order is chosen to be straight? Cause if it was reverse, things would be much easier to express. For instance, the above piece of code could be rewritten as:
ranks.swap(0, ranks.len() - 1);
...which (I think you would agree) is much more readable and cleaner.
Also, it is strange that the similar in the sense of borrowing code compiles succesfully:
let mut vec = vec![1, 2, 3]; // creating vector
vec.push(*vec.last().unwrap()); // DOUBLE BORROWING HERE!!!
// ^ ^
// | |
// | ----- immutable
// ----- mutable
println!("{:?}", vec);
So what the hell is going on? Double standarts, aren't they?
I would like to know the answer. Thank you for explanation.
but isn't it reasonable to suppose that the values of parameters will be calculated before the method starts to do anything and before it somehow can change the content of vector?
...are you sure?
Evaluation order of operands
The following list of expressions all evaluate their operands the same way, as described after the list. Other expressions either don't take operands or evaluate them conditionally as described on their respective pages.
...
Call expression
Method call expression
...
The operands of these expressions are evaluated prior to applying the effects of the expression. Expressions taking multiple operands are evaluated left to right as written in the source code.
So, self is evaluated before ranks.len() - 1. And self is actually &mut self because of auto-ref. So you first take a mutable reference to the vector, then call len() on it - that requires a borrowing it - while it is already borrowed mutably!
Now, this sometimes work (for example, if you would call push() instead of swap()). But this works by magic (called two-phase borrows that splits borrowing into borrowing and activating the reference), and this magic doesn't work here. I think the reason is that swap() is defined for slices and not Vec and thus we need to go through Deref - but I'm not totally sure.

Confusing error message on ? (unwrap operator)

I have this rust method error message:
error[E0277]: the `?` operator can only be used on `Option`s,
not `Result`s, in an async function that returns `Option`
I must admit, I often encounter Rust error messages which appear confusing to me, while to most other coders they make absolute sense.
So, I apologize in advance for posting this question.
First of all: what does that second comma in the error message mean? Should I read it as the following:
"If an async function call [within another function] returns an enum of the type Result then the ? operator can only be applied if, and only if, the respective [other] function also returns an enum of type Result and not an enum of type Option"
Pardon my verbose language. I hope I got my point across.
What also got me confused was the error message with that very same Reference i.e. error[E0277] , which is listed in the official rust error codes index, states:
"You tried to use a type which doesn't implement some trait in a place which expected that trait."
In which universe do these two error messages have anything in common, except for the identical reference number?
And here's the entire error block, which Rust produced:
error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in an async function that returns `Option`
--> src/utils/tokenizer.rs:72:73
|
70 | pub async fn clear(&self) -> Option<String> {
| _________________________________________________-
71 | | let mut conn = self.pool.get().await.unwrap();
72 | | let mut iter: redis::AsyncIter<i32> = conn.sscan("my_set").await?;
| | ^ use `.ok()?` if you want to discard the `Result<Infallible, Red
Error>` error information
73 | | while let Some(element) = iter.next_item().await {
... |
79 | | Some(String::from("A"))
80 | | }
| |_____- this function returns an `Option`
|
= help: the trait `FromResidual<Result<Infallible, RedisError>>` is not implemented for `std::option::Option<std::string::String>`
= help: the following other types implement trait `FromResidual<R>`:
<std::option::Option<T> as FromResidual<Yeet<()>>>
<std::option::Option<T> as FromResidual>
For more information about this error, try `rustc --explain E0277`.
What is the canonical error message, the one from the error code index page or the one, the compiler produces?
Your function clear returns Option<String> but the function conn.sscan("my_set").await?; returns a Result. You cannot use ? to propagate the result because that doesn't match the return type of clear(&self) -> Option<String>.
You will need explicitly handle the Result or convert it to an Option<String>. You could also try adding .ok()? per the compiler hint and see what that gets you.
Whatever you pick the important thing is just to make sure that the return type matches what your ? operator is unwrapping.
Yes, your interpretation is correct, although to be pedantic the error says the opposite:
"If an async function returns Option then the ? operator can only be applied to expressions that has the type Option, and not Result".
And if you ask why on earth this has error code E0277? Well, this is because in order to be propagated using ? a type has to implement the (experimental) Try trait with some combination of type parameters and associated types corresponding to some other combination of the return type of the function. So, in essence, an incompatible type in ? boils down to an unsatisfied trait bound. But unless you're interested in the inner workings of ?, this is not really important.

"Popping" a value from a HashSet

I can't seem to find a way to pop a (random) value from a HashSet. Inspired by other code samples, I wrote the following:
my_set.iter().next().map(|i| my_set.take(i).unwrap())
I.e get an iterator on the set's values, take the first value (a reference) from it, and then run my_set.take() using the reference previously obtained, to get the value itself (not the reference) and remove it from the set.
This doesn't compile due to:
error[E0500]: closure requires unique access to `my_set` but it is already borrowed
|
32 | my_set.iter().next().map(|i| my_set.take(i).unwrap())
| ------ --- ^^^ ------ second borrow occurs due to use of `my_set` in closure
| | | |
| | | closure construction occurs here
| | first borrow later used by call
| borrow occurs here
I've tried many, many variations on this, but they all fail due to an immutable borrow then being borrowed as mutable (error 502).
Can any one recommend a way to rewrite the above?
If you're okay with cloning the item removed you can do:
let elem = set.iter().next().unwrap().clone();
set.remove(&elem);
Playground
Or with a guard against the set being empty:
if let Some(elem) = set.iter().next().cloned() {
set.remove(&elem);
}
Playground
This feels hacky, but you can use HashSet::retain with an impure function:
let mut flag = false;
my_set.retain(|_| mem::replace(&mut flag, true));
(playground)
The element removed is truly arbitrary — I ran the code on the playground a few times, and obtained different results.
As interjay mentioned in a comment, this approach iterates over the entire set just to remove one value, so the cost is generally more significant than cloning one value. Therefore, only use this workaround when the element really cannot be cloned.

"Expected reference, found struct Rc" when cloning and returning an Rc

I have a piece of code like this:
use std::cell::RefCell;
use std::rc::Rc;
struct A(bool);
impl A {
fn get_ref(&self) -> &Rc<RefCell<bool>> {
&Rc::new(RefCell::new(self.0))
}
fn copy_ref(&self) -> &Rc<RefCell<bool>> {
Rc::clone(self.get_ref())
}
}
fn main() {
let a = A(true);
a.copy_ref();
}
and I received warning complaining about the Rc::clone function not getting a reference:
error[E0308]: mismatched types
--> src/main.rs:12:9
|
12 | Rc::clone(self.get_ref())
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected reference, found struct `std::rc::Rc`
|
= note: expected type `&std::rc::Rc<std::cell::RefCell<bool>>`
found type `std::rc::Rc<std::cell::RefCell<bool>>`
I have been working on this all night but I cannot get it to work. The method get_ref is already typed as returning &Rc<RefCell<bool>>, but why would the compiler give the error?
The error is not talking about the argument you put into Arc::clone(), but the whole expression Rc::clone(...) which has a different type (Rc<...>) than the return type of your function (&Rc<...>).
If you were passing a wrong argument to Rc::clone, it would like look this:
--> src/main.rs:13:19
|
13 | Rc::clone(false)
| ^^^^^ expected reference, found bool
|
= note: expected type `&std::rc::Rc<_>`
found type `bool`
So the naive way to fix the type error is to write &Rc::clone(...) instead. Then the last expression of your function has the same type as your function's declared return type. But as you will notice, you will get other errors afterwards.
Let's take a step back to see that your approach is flawed here. For the most important point, please see "Is there any way to return a reference to a variable created in a function?". Spoiler: you really don't want to. So constructs like your get_ref() just don't make sense, as you return a reference to a variable you create inside your function (a variable of type Rc).
In your case the direct solution is probably pretty simple: just remove the reference. Rc<T> is already a pointer/reference type, so there is no need (in general) to have a reference to it.
However, since you are using Rc, you are probably interested in reference counting. So in that case, you probably shouldn't create a new Rc every time the function is called. Otherwise you could end up with a bunch of Rcs with reference count 1, which is not really the point. So instead, your type A should already store an Rc<RefCell<bool>>.
But all I'm doing here is guessing what you actually want to do which is not clear from your question. Maybe you can ask a different question, or add the information to this question, or explain this in the comments.

Resources