How to return error as value from failable function? - error-handling

The current release of Zig is 0.10.1 at the time of asking this question. The language is not yet stable and is subject to change.
How can I return an error as value from a failable function? I have tried the following, but it returns it as an error instead of returning it as a value:
fn foo_1() !anyerror {
// Returns outer error.
return error.Oops;
}
fn foo_2() !anyerror {
// Returns outer error.
var error_ = error.Oops;
return error_;
}
I would like to return it as value such that the following code to print out the error instead of propagating it when try is evaluated:
fn bar() !void {
var error_ = try foo();
std.debug.print("got error: {any}\n", .{ error_ });
}
I am asking this because I am trying to learn the language. I don't have a concrete use-case for this. However, I am curious if this is possible to do.
A similar related situation is to return an optional value which itself is optional (for example when forwarding the result of a function which returns an optional), but I was already able to find a solution for that:
fn baz_1() ??i32 {
// The outer optional is null.
return null;
}
fn baz_2() ??i32 {
// The inner optional is null.
var value: ?i32 = null;
return value;
}
Is something like that possible for errors as well?

No, it's not allowed in Zig. If you try you'll get an error like "error union with payload of error set type 'error{Def}' not allowed".
However, you can wrap the error in a struct:
const std = #import("std");
const ErrorError = error{
Abc,
};
const ErrorValue = error{
Def,
};
const WrappedError = struct {
err: ErrorValue,
};
fn foo() ErrorError!WrappedError {
return .{
.err = ErrorValue.Def,
};
}
test "returns error value" {
var bar = try foo();
std.debug.assert(bar.err == ErrorValue.Def);
}

error values can be declared with error{...} syntax. see https://ziglang.org/documentation/master/#Errors
pub const Error = error{Bar};
fn foo() error{Bar} {
return error.Bar;
}
// or anyerror
fn foo() anyerror {
return error.Bar;
}

Related

How can I print data in a way that consumes local variables when an assert fails in Rust?

I have some tests which have some variables that hold some important data and I'd like to print their data when an assertion fails. Getting the data I need consumes the variables, so the printing code must own the variables. In this example, I'd want to call dump_foo_data once an assertion fails:
struct Foo();
fn dump_foo_data(f: Foo) {
eprintln!("Behold, Foo data: ");
}
#[test]
fn my_test() {
let f = Foo();
eprintln!("begin");
// do a test
&f;
let success = true;
assert!(success);
// do another test
&f;
let success = false;
assert!(success);
}
I can make a very bad solution by making dump_foo_data non-returning and panic:
fn dump_foo_data(f: Foo) -> ! {
eprintln!("Behold, Foo data: ");
panic!();
}
Then instead of using assert!, I check the failure with an if and maybe call dump_foo_data:
let success = true;
if !success {
dump_foo_data(f);
}
This is too many lines of code, and I need to specify f. In reality, I have more than one variable like f that I need to dump data from, so it's not very nice to list out single relevant local variable in every check.
I couldn't figure out how to write a macro to make this better because I'd still need to pass every relevant local variable to the macro.
I couldn't think of a way to use std::panic either. update_hook would need to take ownership of f, then I couldn't use it in tests.
Is there any good way to do this in Rust?
Edit: I've thought of another approach: put each relevant local in an Rc then pass each of those to std::panic::update_hook. I've not confirmed whether this'll work yet.
Edit 2: Maybe I could abuse break to do what I explained with goto in a comment.
One way that doesn't use any macro or shared-interior-mutability-reference magic might be to repossess f:
fn check_or_dump(success: bool, f: Foo) -> Foo {
match success {
true => f,
false => panic!("Behold foo data: {:?}", dump_foo_data(f)),
}
}
You use it like this:
let f = Foo();
let success = true;
let f = check_or_dump(success, f);
let success = false;
let f = check_or_dump(success, f);
// and so on.
Here's a solution without macro or interior mutability and that doesn't require you to list all the variables on each check. It is inspired by this answer:
struct Foo();
fn dump_foo_data(_f: Foo) {
eprintln!("Behold, Foo data: ");
}
#[test]
fn my_test() {
let f = Foo();
let doit = || -> Option<()> {
eprintln!("begin");
// do a test
&f;
let success = true;
success.then_some(())?;
// do another test
&f;
let success = false;
success.then_some(())?;
Some(())
};
if let None = doit() {
dump_foo_data (f);
panic!("Test failure");
}
}
Playground
I've worked out a solution using the panic handler:
use std::rc::Rc;
use std::cell::{Cell, RefCell};
use std::panic::PanicInfo;
thread_local! {
static TL_PANIC_TARGETS: RefCell<Vec<Rc<dyn PanicTrigger>>> = RefCell::new(vec![]);
}
pub trait PanicTrigger {
fn panic_trigger(self: Rc<Self>);
}
pub fn register_panic_trigger<P: PanicTrigger + 'static>(p: Rc<P>) {
TL_PANIC_TARGETS.with(|v: _| {
v.borrow_mut().push(p.clone());
});
}
#[ctor::ctor]
fn set_panic_hook() {
let old_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |pi: &PanicInfo| {
run_panic_triggers(pi);
old_hook(pi);
}));
}
fn run_panic_triggers(_: &PanicInfo) {
TL_PANIC_TARGETS.with(|v: _| {
for pt in v.take() {
pt.panic_trigger();
}
});
}
struct Foo();
fn dump_foo_data(_f: Foo) {
eprintln!("Behold, Foo data: ");
}
impl PanicTrigger for Cell<Option<Foo>> {
fn panic_trigger(self: Rc<Self>) {
if let Some(f) = self.take() {
dump_foo_data(f);
}
}
}
#[test]
fn my_test() {
let f = Rc::new(Cell::new(Some(Foo())));
register_panic_trigger(f.clone());
let success = true;
assert!(success);
let success = false;
assert!(success);
}
fn main() { }
Basically, you put the relevant data in an Rc and keep a local reference and put one in TLS for the panic handler. You need to put it in an Option in a Cell so that you can move out of it.
Types that don't need to be owned to print relevant data can be registered too, and you don't need to implement PanicTrigger on a Cell<Option<T>>, just T.
This is thread-safe.
Because the data is so wrapped up, it's harder to manipulate in the test body. But now you can use normal assert!. It's a trade-off.

How to fold results from a few async calls

I want to get the results of my asynchronous function and use them in the fold function. Here's my function that didn't work:
private fun sendOrderStatus(list: List<OrderStatusEntity>) : Single<Boolean> {
return Single.just(
list.fold(true) { initial, item ->
if (!item.isTerminal()) {
val info = OrderStateParameters(
lon = item.orderStatusLon!!,
lat = item.orderStatusLat!!,
datetime = item.orderStatusDate!!
)
val state = OrderState(item.orderId, item.toSend(), info)
return tasksUseCase.sendOrderStatusForWorker(state)
.doOnSuccess { markSent(item) } // side calling
.flatMap {
return#flatMap initial && it.isSuccess // that result should be used in *fold*-function
}
} else // stub result
true
}
)
}
So, I intend to return a Single that will contain the aggregated result of all tasksUseCase.sendOrderStatusForWorker(state) calls.
Thank for any helps!

Return a Result from a for loop or nothing if there are no results

I want to return the Result as shown in below from the for loop. Please help which would be the best way solve this error. I tried the pattern matching with returning None which works. But I need to return Error.
pub fn get_account(&self) -> Result<Keys, Error> {
//PATH is default home directory
let values = match load_json_file(PATH + "/keys.json") {
Ok(account) => Ok(account),
Err(e) => {
return Err(Error::Invalid_Tx(
"The sender address cannot be nil".to_owned(),
))
}
};
let accounts: Vec<Keys> = values.unwrap();
let sender_address = self.sender.unwrap();
for acc in accounts {
if acc.address == sender_address {
return Ok(acc);
};
};
Ok(())
}
expected struct commands::key::Keys, found ()rustc(E0308)
You are trying to return two different types from the same function:
line 15: Ok(acc) is of type Result<Keys, Error>
line 18: Ok(()) has type Result<(), Error>
If "no result" is a valid return value, then you can change the function signature to:
pub fn get_account(&self) -> Result<Option<Keys>, Error>;
And then modify those return values to be Ok(Some(acc)) and Ok(None) respectively.
If "no result" is an error then you need to modify the Error type to include this variant. For example:
enum Error {
Invalid_Tx(String),
NotFound,
}
And return Err(Error::NotFound) at the end.
You can also tidy this function up a lot, by using thiserror, which is a popular crate for defining error types:
use thiserror::Error; // thiserror = "1.0.21"
#[derive(Debug, Error)]
enum Error {
#[error("The sender address cannot be nil")]
InvalidTx,
#[error("The key was not found")]
NotFound,
}
pub fn get_account(&self) -> Result<Keys, Error> {
let accounts: Vec<Keys> = load_json_file(PATH + "/keys.json")
.map_err(|_| Error::InvalidTx)?;
let sender_address = self.sender.unwrap();
accounts
.into_iter()
.find(|acc| acc.address == sender_address)
.ok_or(Error::NotFound)
}
This is better because the Strings in the errors do not need to be allocated, but they are still available as static string slices if they are needed for display. I also got rid of the for loop altogether, which makes the function shorter and cleaner.

Error "Out of segment space" in VMEmulator cause by a getter mwthod in Jack

I am doing a project for nand2tetris. We write a program in Jack and test it on VMEmulator. The class looks like this:
class List {
field int data;
field List next;
/* Creates a new List object. */
constructor List new(int car, List cdr) {
let data = car;
let next = cdr;
return this;
}
/* Disposes this List by recursively disposing its tail. */
method void dispose() {
if (~(next = null)) {
do next.dispose();
}
// Use an OS routine to recycle the memory held by this object.
do Memory.deAlloc(this);
return;
}
/* Prints the list*/
method void print() {
do Output.printString(" -> ");
do Output.printInt(data);
if (~(next = null)) {
do next.print();
}
return;
}
/* Inserts the argument in the right position of the list (ascending order)*/
method void insertInOrder(int ins){
var List prev, curr, insert;
let prev = this;
let curr = prev.getnext();
while (ins > prev.getdata()){
if (ins < curr.getdata()){
let insert = List.new(ins, curr);
do prev.setnext(insert);
}
else{
let prev = prev.getnext();
let curr = prev.getnext();
}
}
return;
}
/* Searches the argument in the list, if found, it returns the corresponding List object*/
method List find(int toFind){
var List temp;
var List equal;
var boolean found;
let temp = this;
let found = false;
while (~(next = null)){
if(toFind = temp.getdata()){
let equal = temp;
let found = true;
}
let temp = temp.getnext();
}
if (found){
return equal;
}
else{
return null;
}
}
method List getnext(){
return next;
}
method void setnext(List object){
let next = object;
return;
}
method int getdata(){
return data;
}
}
It has one private variable data and a pointer next. So I wrote getter and setter method to return those values. Other methods are fine only the getdata()method is incorrect. When it runs through the VMEmulator, it shows the error Out of segment space in List.getdata.3. This shows in the VMEmulator.
0function List.getdata0
1push argument0
2pop pointer0
3push this 0
4return
the error is at the 4th line return. When I change the Jack code, the same error is still at the 4th line.
What exactly is the problem in my getter method?
When you run a VM program on the VMEmulator you must first manually set the pointers to the various segments, otherwise you may get an "Out of segment space" error.
To understand the necessary settings, look at what the corresponding .tst file does. An alternative method is to insert the proposed code inside a function, since the function call automatically makes this type of setting.
You can get this error when you try to access member data of an object which is not constructed. Could it be that the List cdr in the constructor was not properly constructed?

Return Option inside Loop

The program aims to use a loop to check if the index of a iterator variable meets certain criteria (i.g., index == 3). If find the desired index, return Some(123), else return None.
fn main() {
fn foo() -> Option<i32> {
let mut x = 5;
let mut done = false;
while !done {
x += x - 3;
if x % 5 == 0 {
done = true;
}
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
return None; //capture all other other possibility. So the while loop would surely return either a Some or a None
}
}
}
The compiler gives this error:
error[E0308]: mismatched types
--> <anon>:7:9
|
7 | while !done {
| ^ expected enum `std::option::Option`, found ()
|
= note: expected type `std::option::Option<i32>`
= note: found type `()`
I think the error source might be that a while loop evaluates to a (), thus it would return a () instead of Some(123). I don't know how to return a valid Some type inside a loop.
The value of any while true { ... } expression is always (). So the compiler expects your foo to return an Option<i32> but finds the last value in your foo body is ().
To fix this, you can add a return None outside the original while loop. You can also use the loop construct like this:
fn main() {
// run the code
foo();
fn foo() -> Option<i32> {
let mut x = 5;
loop {
x += x - 3;
for (index, value) in (5..10).enumerate() {
println!("index = {} and value = {}", index, value);
if index == 3 {
return Some(123);
}
}
if x % 5 == 0 {
return None;
}
}
}
}
The behaviour of while true { ... } statements is maybe a bit quirky and there have been a few requests to change it.