While closures are powerful, they can lead to bugs if used carelessly. An example is a closure that modifies its captured variables when this is not wanted. Sometimes we only need an anonymous function that has no free variables. That is, it captures nothing. Can I specify a closure as capturing nothing and get this checked by the compiler? Something like none |...| {...} looks ideal. I know I can define a function in current scope but it's not as elegant as a variable containing an anonymous function.
Only non capturing closures can coerce to function pointers. You can use this fact to check that the closure is non-capturing by attempting to coerce it to a function pointer:
let closure = || {};
let _: fn() = closure;
You can wrap it in a macro to make this convenient:
macro_rules! enforce_non_capturing {
// This macro does not allow specifying the return type (`|| -> Ret {}`),
// but it can be adjusted to allow that.
// It also does not allow patterns as parameter names, but allowing that
// is harder.
(
| $( $param:ident $( : $param_ty:ty )? ),* $(,)? | $body:expr
) => {{
let closure = | $( $param $( : $param_ty )?, )* | $body;
// We want to generate `fn(_, _, ...) -> _` with underscores as the number of parameters.
// We use a dummy repetition to achieve that.
let _: fn( $( enforce_non_capturing!(#replace_with_underscore $param ), )* ) -> _ = closure;
closure
}};
// `||` is recognized as one token instead of two, so we need another arm.
( || $body:expr ) => { enforce_non_capturing!(| | $body) };
(#replace_with_underscore $param:ident) => { _ };
}
fn main() {
// Compiles.
let closure = enforce_non_capturing!(|| {});
closure();
let a = 0;
// Doesn't compile.
// let closure = enforce_non_capturing!(|| a);
// closure();
}
Playground.
Maybe the constraint could not be expressed where the closure/function is provided, but where it is expected.
In this example, fnct2() cannot receive a closure.
fn fnct1(mut f: impl FnMut(i32) -> i32) {
println!("{}", f(10));
}
fn fnct2(f: fn(i32) -> i32) {
println!("{}", f(20));
}
fn main() {
let mut x = 0;
fnct1(|n| {
x += 1;
n + x
});
fnct2(|n| {
// x += 2; // ERROR: expected fn pointer, found closure
// n + x
n + 2
});
println!("x={}", x);
}
/*
11
22
x=1
*/
While this is most certainly not a 'best practice', you can store the closure in a function pointer. Function pointers can only hold non-capturing closures.
This works:
fn main() {
let c: fn() = || {
println!("Hello world!");
};
c();
}
Hello world!
While this doesn't:
fn main() {
let mut a = 10;
let c: fn() = || {
a += 1;
};
}
error[E0308]: mismatched types
--> src/main.rs:3:19
|
3 | let c: fn() = || {
| ____________----___^
| | |
| | expected due to this
4 | | a += 1;
5 | | };
| |_____^ expected fn pointer, found closure
|
= note: expected fn pointer `fn()`
found closure `[closure#src/main.rs:3:19: 3:21]`
note: closures can only be coerced to `fn` types if they do not capture any variables
--> src/main.rs:4:9
|
4 | a += 1;
| ^ `a` captured here
Related
In the code below, version 1 does not compile, whereas version 2 does.
fn foo(text: String) -> Result<u32, String> {
let mut acc: u32 = 0;
for string in text.split("_") {
let result: Option<u32> = string.parse::<u32>().ok();
// version 1
let x: u32 = result.ok_or(text)?;
acc += x;
// version 2
// if let Some(x) = result {
// acc += x;
// } else {
// return Err(text)
// }
}
Ok(acc)
}
error[E0382]: use of moved value: `text`
--> src/main.rs:102:35
|
96 | fn foo(text: String) -> Result<u32, String> {
| ---- move occurs because `text` has type `String`, which does not implement the `Copy` trait
...
102 | let x: u32 = result.ok_or(text)?;
| ^^^^ value moved here, in previous iteration of loop
The issue is that I'm moving text into another function (ok_or) on each loop iteration.
So I understand the error message, but is there still a way out to use the shorthand ? notation in this case? The version 2 is the shortest I could get but it still seems too verbose.
(this is just a MWE / toy example, my question is not about summing numbers in a string)
If you cannot afford a copy on the Err case, and you have a lot of places like that, you can use a macro:
macro_rules! try_opt {
( $v:expr, $e:expr $(,)? ) => {
match $v {
Some(v) => v,
None => return Err($e),
}
}
}
let x: u32 = try_opt!(result, text);
If you can afford a string clone in the Err case (not the happy path), you can just take a reference, as String implements From<&String>:
let x: u32 = result.ok_or(&text)?;
I have a function like the following:
pub fn process_options<T>(in_refs:& mut Vec<T>,ivals:Vec<Option<T>>) -> i32 {
let mut nones = 0;
for int in 0..ivals.len() {
if let None = ivals[int] {
nones += 1;
} else {
in_refs.push(ivals[int].unwrap());
}
}
return nones;
}
What this function does is that it:
It pushes all the some(T) from one vector to another as T. And it also records the number of none values in the first vector. As written above the code fails to compile and gives me:
error[E0507]: cannot move out of index of `Vec<Option<T>>`
--> src/functions.rs:40:26
|
40 | in_refs.push(ivals[int].unwrap());
| ^^^^^^^^^^ -------- value moved due to this method call
| |
| help: consider calling `.as_ref()` or `.as_mut()` to borrow the type's contents
| move occurs because value has type `Option<T>`, which does not implement the `Copy` trait
|
note: this function takes ownership of the receiver `self`, which moves value
--> /Users/yunfeichen/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/library/core/src/option.rs:772:25
|
772 | pub const fn unwrap(self) -> T {
Which I frankly do not understand can you explain this to me?
Edit:
This question has been solved:
It turns out I just need to use a different style of for loop, Not sure why this for loop works but it does:
pub fn process_options<T>(in_refs:& mut Vec<T>,ivals:Vec<Option<T>>) -> i32 {
let mut nones = 0;
for element in ivals{
if let None = element {
nones += 1;
} else {
in_refs.push(element.unwrap());
}
}
return nones;
}
C++ example:
for (long i = 0; i < 101; i++) {
//...
}
In Rust I tried:
for i: i64 in 1..100 {
// ...
}
I could easily just declare a let i: i64 = var before the for loop
but I'd rather learn the correct way to doing this, but this resulted in
error: expected one of `#` or `in`, found `:`
--> src/main.rs:2:10
|
2 | for i: i64 in 1..100 {
| ^ expected one of `#` or `in` here
You can use an integer suffix on one of the literals you've used in the range. Type inference will do the rest:
for i in 1i64..101 {
println!("{}", i);
}
No, it is not possible to declare the type of the variable in a for loop.
Instead, a more general approach (e.g. applicable also to enumerate()) is to introduce a let binding by destructuring the item inside the body of the loop.
Example:
for e in bytes.iter().enumerate() {
let (i, &item): (usize, &u8) = e; // here
if item == b' ' {
return i;
}
}
If your loop variable happens to be the result of a function call that returns a generic type:
let input = ["1", "two", "3"];
for v in input.iter().map(|x| x.parse()) {
println!("{:?}", v);
}
error[E0284]: type annotations required: cannot resolve `<_ as std::str::FromStr>::Err == _`
--> src/main.rs:3:37
|
3 | for v in input.iter().map(|x| x.parse()) {
| ^^^^^
You can use a turbofish to specify the types:
for v in input.iter().map(|x| x.parse::<i32>()) {
// ^^^^^^^
println!("{:?}", v);
}
Or you can use the fully-qualified syntax:
for v in input.iter().map(|x| <i32 as std::str::FromStr>::from_str(x)) {
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
println!("{:?}", v);
}
See also:
How do I imply the type of the value when there are no type parameters or ascriptions?
There used to be a discussion where this was requested, which was followed up by an actual RFC.
It seems like the discussion was postponed, though, because not enough people really cared about the topic.
Currently, if you absolutely want to annotate, it seems like the best option you have is:
fn main() {
let my_vec: Vec<i32> = vec![-1, 22, -333];
for i in my_vec.iter() {
let _: &i32 = i;
println!("{}", i);
}
}
As you can see, this fails if the type doesn't match:
fn main() {
let my_vec: Vec<i32> = vec![-1, 22, -333];
for i in my_vec.iter() {
let _: &i16 = i;
println!("{}", i);
}
}
--> src/main.rs:4:23
|
4 | let _: &i16 = i;
| ---- ^ expected `i16`, found `i32`
| |
| expected due to this
|
= note: expected reference `&i16`
found reference `&i32`
Of course due to automatic dereferencing this method cannot differentiate between &i32 and &&i32, which might be a problem in some cases:
fn main() {
let my_vec: Vec<i32> = vec![-1, 22, -333];
for i in my_vec.iter() {
let _: &i32 = &i; // Compiles, but the right side is &&i32
println!("{}", i);
}
}
But in general this should bring enough confidence to potential reviewers, in my opinion.
Try casting with as:
for i in 1..100 as i64 {
// ...
}
The following program compiles without issue:
#[tokio::main]
async fn main() {
async fn g(x: String) {}
let f = || {
let y: String = String::from("a").clone();
return async {
println!("{}", &y);
return g(y).await;
}};
}
However, if the line "return g(y).await;" is removed, it will fail with the following:
error[E0373]: async block may outlive the current function, but it borrows `y`, which is owned by the current function
--> src/main.rs:35:22
|
35 | return async {
| ______________________^
36 | | println!("{}", &y);
| | - `y` is borrowed here
37 | | // return g(y).await;
38 | | }};
| |_____^ may outlive borrowed value `y`
|
note: async block is returned here
--> src/main.rs:35:16
|
35 | return async {
| ________________^
36 | | println!("{}", &y);
37 | | // return g(y).await;
38 | | }};
| |_____^
help: to force the async block to take ownership of `y` (and any other referenced variables), use the `move` keyword
|
35 | return async move {
| ++++
Why does the same error not appear in the original code?
Rust does the minimum amount of work necessary to get your closure to work.
let f = || {
let y: String = String::from("a").clone();
return async {
println!("{}", &y);
}
};
Here, the inner closure requires y by reference. So Rust is going to turn it into, essentially, a struct with a &String. Removing the async stuff for simplicity, it would turn this
let f = || {
let y: String = String::from("a").clone();
|| {
println!("{}", &y);
}
};
into, effectively, this
struct MyCustomClosure<'a> { y: &'a String };
impl<'a> FnOnce for MyCustomClosure<'a> {
fn call_once(self) {
println!("{}", self.y)
}
}
// (Same impl for FnMut and Fn ...)
let f = || {
let y: String = String::from("a").clone();
return MyCustomClosure { y: &y }
};
Now, sometime way later on in the compilation process, Rust realizes that the lifetime 'a for MyCustomClosure doesn't line up with the lifetime for the enclosing function, and it complains. But by this point it's already committed to using a reference here and it's not smart enough to go back and try a different closure type. It's two different stages of compilation that don't talk to each other directly.
This on the other hand
let f = || {
let y: String = String::from("a").clone();
|| { y }
};
This, on the other hand, very clearly requires a move. We're passing ownership inside the closure, so we get a closure that only implements FnOnce and that takes the y by value. Essentially we get
struct MyCustomClosure2 { y: String };
impl FnOnce for MyCustomClosure2 {
fn call_once(self) -> String {
self.y
}
}
// No FnMut or Fn this time, since we need to pass ownership of a value.
Now there's no lifetime argument 'a to cause conflicts down the road. There's just a simple struct and it all works out.
As the error message indicates, if your intent is to get an FnOnce which returns the string by moving, you can prefix your closure with the move keyword.
let f = || {
let y: String = String::from("a").clone();
return async move {
println!("{}", &y);
}
};
In the definition of lifetime_things, the lifetime of 'b is longer than 'a, but actually when I call this function, x1 is longer than y1, but this can compile successfully:
//here you could see 'b:'a means, the lifetime of b should be larger than a,
fn lifetime_things<'a,'b:'a>(x:&'a String, y:&'b String) ->&'a String {
if x.len() > y.len() {
&x
} else {
&y
}
}
fn main() {
let z: i32;
let x = String::from("1");
let x1=&x;
{
let y = String::from("2");
let y1=&y;
println!("{}",lifetime_things(x1,y1));
}
}
But here you could see the lifetime of x1 should be larger than y1 so why can this compile successfully as well?
'b: 'a means 'b must outlive 'a, which in turn means whenever 'a is live, so must 'b. Crucially, this is a larger than or equals relationship. Also notice that lifetimes don't work like types. When a function takes an 'v reference, any lifetime ('w) will be accepted as long as 'w: 'v.
So, where is 'a live? In the body of the function and whenever the returned string reference is used after the call. We can visualize it like this:
fn lifetime_things<'a,'b:'a>(x:&'a String, y:&'b String) ->&'a String {
if x.len() > y.len() { // <-+
&x // |
} else { // |
&y // +----+
} // | |
} // <-+ |
// |
fn main() { // +-- 'a
let z: i32; // |
let x = String::from("1"); // |
let x1=&x; // |
{ // |
let y = String::from("2"); // |
let y1=&y; // |
println!("{}",lifetime_things(x1,y1)); // <------+
}
}
Notice, that since the value returned by lifetime_things is only printed, 'a is only live on the line println!("{}",lifetime_things(x1,y1)); (exclusing lifetime_thingss body).
So, to call lifetime_things we simply need the both arguments to at least be live at the call. This holds for both x1 and y1, so it all checks out.