Rust mapping wants me to put a block - rust

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 ->.

Related

Get absolute maximum value from f64 array

I have an array, let's say:
let arr: Vec<f64> = vec![1.5, 1.1, -1.6, 0.9, -0.5];
I want the absolute maximum of this array:
let abs_max = arr."some functions";
println!("{}", abs_max); // Gives 1.6 (or -1.6, but not 1.5).
Is there a smart, maybe almost inline, way of doing this? Or do i have to create my own function which iterates through all the values by for loop and compares the values?
I've tried the following suggestion made in this post:
let abs_max = arr.iter().max_by(|a, b| a.abs().total_cmp(b.abs()))
And this code gives the following error:
error[E0599]: no method named `abs` found for reference `&&{float}` in the current scope
--> src\main.rs:51:44
|
51 | let abs_max = arr.iter().max_by(|a, b| a.abs().total_cmp(b.abs()));
| ^^^ method not found in `&&{float}`
error[E0599]: no method named `abs` found for reference `&&{float}` in the current scope
--> src\main.rs:51:62
|
51 | let abs_max = arr.iter().max_by(|a, b| a.abs().total_cmp(b.abs()));
| ^^^ method not found in `&&{float}`
In Rust, we usually operate on iterators rather than collections like vectors directly, so all of the juicy "operate on a collection" functions are going to be in std::iter::Iterator.
Now, Iterator has a max function, which would work for integers, but it won't work for floats, because floats are not totally ordered (NaN, as usual, is a problem).
If you had a collection of integers or anything else that implemented the Ord trait, you could write
arr.iter().max()
which would return an Option<T> containing the maximum value, or None if the collection was empty.
However, f64 and the other floating-point types don't implement Ord. Fortunately, the wonderful writers of the Rust documentation for Iterator::max thought of this.
Note that f32/f64 doesn’t implement Ord due to NaN being incomparable. You can work around this by using Iterator::reduce:
assert_eq!(
[2.4, f32::NAN, 1.3]
.into_iter()
.reduce(f32::max)
.unwrap(),
2.4
);
So we can apply reduce to f64::max (which is like Ord::max except that it works for the non-total ordering of f64). Adapting a bit to your use case, we get
arr.iter().copied().reduce(f64::max)
Again, this returns an Option<f64> which is empty if the initial collection is empty. You can unwrap if you know the initial collection to be nonempty. Also, if you're never using the array again after this point (i.e. you can pass ownership of it to the iterator), you can use arr.into_iter() rather than arr.iter().copied() to save yourself a copy of each element.
This compares using f64::max, the "usual" ordering of f64. But it sounds like you want a custom ordering, namely the maximum by absolute value. We can use max_by to get this custom ordering.
arr.iter().max_by(|a, b| a.abs().total_cmp(&b.abs()))
Finally, if this is a large project and you don't mind pulling in external dependencies, I recommend OrderedFloat for dealing with all of the awkwardness around NaN and floating point types. It provides an Ord instance for a float-like type which sorts NaN as part of the total ordering, rather than following the (frankly bizarre) IEEE rules for ordering floats. With that library, your maximum becomes
arr.iter().max_by_key(|x| OrderedFloat(x.abs())

Fold with string array

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.

"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.

Returning from inside for loop causes type mismatch

I am attempting to return a function pointer, which is located inside a for loop, from a function located in an impl of a struct.
fn locate_func(&self, string: &str) -> fn() -> bool {
let mut func;
for alt in &self.alts {
return alt.func;
}
}
There will be an if statement inside the for loop in the future, but as I am testing things at the very moment, it looks rather generic, and somewhat illogical.
The above code in my mind, is supposed to return the pointer to alt.func(), which clearly is a pointer, as it tells me so should I remove the return and semicolon of that line.
error[E0308]: mismatched types
--> src\main.rs:42:3
|
42 | for alt in &self.alts
| ^ expected fn pointer, found ()
|
= note: expected type `fn() -> bool`
= note: found type `()`
Above is the error that is caused upon running locate_func(). I am clearly missing something as the aforementioned code is not working properly. Any hints?
Your for-loop is the last expression inside the function. The compiler expects the last expression to evaluate to the return type. But all loops evaluate to () (unit or void), so the compiler has a classic type mismatch there.
The correct question to ask yourself is: what would happen if the return inside of the loop wouldn't be executed (for example, because the loop isn't executed at all, because self.alts is empty)? This would lead to problems, wouldn't it?
So you have to return a valid object after the for-loop to cover that case. But if you are certain that the spot after the loop will never be reached you can use unreachable!(); to tell the compiler what you already know. However, if the program will reach this spot, it will panic! So better make sure, you know for certain how the program behaves.

Compile error when trying to use increment operator

During work on a side project I've tried to use an increment operator, as following:
fn main() {
let mut my_var = 5;
my_var++;
}
and received the following error:
error: expected expression, found `+`
--> src\main.rs:3:12
|
3 | my_var++;
| ^
What's wrong with my code?
Increment (++) and decrement (--) operators are not supported in Rust.
From Rust's FAQ:
Why doesn't Rust have increment and decrement operators?
Preincrement and postincrement (and the decrement equivalents), while
convenient, are also fairly complex. They require knowledge of
evaluation order, and often lead to subtle bugs and undefined behavior
in C and C++. x = x + 1 or x += 1 is only slightly longer, but
unambiguous.

Resources