41

I have the following simplified code:

fn f() -> i32 { let a = some_result.unwrap_or_else(|_| { return 1; // want to return this value from f <------------- }); } 

I want to return the value 1 from the whole function f in this specific error case but I can't figure out how to do it from within a closure.

If I instead use a match expression, it works fine as follows:

fn f() -> i32 { let a = match some_result { Ok(result) => result, Err(_) => { return 1; }, }; } 

However, this makes the code verbose since I have the trivial Ok match arm.

1
  • You could just go, .unwrap_or(1) Commented Mar 8, 2019 at 22:56

4 Answers 4

34

No, there is not.

A closure is a method (a kind of function) under the hood. You are asking for the ability to exit a parent function from an arbitrarily deeply nested function call. Such non-local flow control has generally proven to be extremely bad for programmer sanity and program maintenance.


To solve your problem:

Sign up to request clarification or add additional context in comments.

1 Comment

That makes sense. I was just hopeful that there was something similar to Kotlin's return@ in the case of a closure parameter
5

I would like to show you a trick.

If you make a mutable valuable for the exception case, you can set the value in the closure. And then, you could make the function returning specific value.

fn f() -> i32 { let mut invalid: bool = false; // + It is for the exception case let a = some_result.unwrap_or_else(|_| { // return 1; // want to return this value from f <------------- invalid = true; // + If the case is exceptional, }); if invalid { // + If the value is marked, return specific value return 1; } ... } 

Because a closure is a method, there is no way exactly. I would like to comment that it is just a trick.

1 Comment

In this exact case you gave, you should not use a closure and just use a match statement does
3

These days, this specific example is also solveable with pattern matching:

fn f(some_result: Result<i32, ()>) -> i32 { let Ok(a) = some_result else { return 1; // can actually return this value from f <------------- }); a } 

Comments

-1

One way to jump accross function boundaries is using std::panic::resume_unwind to trigger panicking without invoking the global panic hook and std::panic::catch_unwind to catch the panic and inspect its payload, similar to catching exceptions.

If you want to return early upon inspecting an Err, this obviously isn't the solution.

However, there may be extreme situations where jumping accross function boundaries may be desirable:

  • You want your closure to short-circuit code from a third-party library not supporting short circuting via closures returning Option/Result/ControlFlow.
  • You want your closure to report errors back but the third-party library does not provide a method for calling code to functionally obtain those errors.

Before using panics as control flow, consider other solutions:

  • Rewriting APIs to accept short-circuiting
  • Use &mut T or interior mutability to record errors
  • Find alternatives.

This solution is not recommend for having substantial disadvantages:

  • Unwinding is not always available e.g. if code is compiled with panic = "abort". If your code can be compile without unwinding, you will have to provide another solution anyway.
  • std::panic::catch_unwind is optimized for the no-panicking path. If not short-circuiting is not a performance issue, then adding std::panic::catch_unwind and std::panic::resume_unwind may actually worsen performance.
  • The panic must occur within the std::panic::catch_unwind call. It can be ambiguous when the closure will actually run due to lazy evaluatio and if you can choose when it will. If the panic occurs outside of the std::panic::catch_unwind call, then all this machinery is pointless.
  • This solution may look complicated and unidiomatic.

If you have considered all possible solutions and decided that std::panic::catch_unwind may be a solution, below is an example of the pattern:

// Only valid if unwinding is enabled #[cfg(panic = "unwind")] fn f() -> i32 { let some_result = Err(()); // Struct to mark the break signal specific to this code struct Break; // Catch all panics match std::panic::catch_unwind(|| some_result.unwrap_or_else(|_| { // Trigger panicking with the break signal as payload without invoking the global panic hook. std::panic::resume_unwind(Box::new(Break)) }) ) { Ok(i) => i, // Inspect the payload if panic was thrown. // If you are certain nothing inside std::panic::catch_unwind other than your signal can panic, // you can use panic!() or std::panic::resume_unwind(Box::new(())) instead, // skip checking the type of the payload and just return the default value. Err(e) => // If its the specified signal, return a default value if e.is::<Break>() { 1 } // Else resume panicking with that payload else { resume_unwind(e) }, } } 

2 Comments

This is never the solution.
The post asked whether there are "any way to return from a function from inside a closure" and panic is the only such construct in Rust. I have already elaborate why this is not a good idea, so I have you any more issues or other ideas to jump closure boundaries, please elaborate any further.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.