Wondering if there is a better way to handle panic!

In the following code, 'point' is returned, but to avoid the 'possibly not initialized error' I initialized it to a ridiculous value and then invoked panic! with a bool. I don't want the caller of this function to handle an empty point since it should never be empty, so I use panic! in hopes of satisfying the compiler.

Is there a better way to implement panic here so I don't need the (-1,-1) initialization?

This function takes an x, y mouse coordinate and checks if it falls within any of 64 rectangles:

fn get_clicked_square_id(x: f64, y: f64, scalar: usize) -> (i32, i32) {
    let mut counter = 0;
    let x2 = scalar;
    let y2 = scalar;
    let x = x as usize;
    let y = y as usize;
    let mut point = (-1, -1); 
    let mut square_not_found = true;

    for x1 in (0..456).step_by(scalar) {
        for y1 in (0..456).step_by(scalar) {
            counter += 1;
            println!("value of iterator is: {}> ({}, {})", counter, x1, y1);
            if x1 < x && x < x2 && y1 < y && y < y2 {
                point = (x1 as i32, y1 as i32);
                square_not_found = false;
                break;
            }
        }
    }
    if square_not_found {
        panic!("Square ID not found");
    }else {
        point
    }
} 
1 Like

The easy thing to do would be just return (x1 as i32, y1 as i32); when you find the point. Then just panic if you ever leave the loop at the bottom.

fn get_clicked_square_id(x: f64, y: f64, scalar: usize) -> (i32, i32) {
    let mut counter = 0;
    let x2 = scalar;
    let y2 = scalar;
    let x = x as usize;
    let y = y as usize;

    for x1 in (0..456).step_by(scalar) {
        for y1 in (0..456).step_by(scalar) {
            counter += 1;
            println!("value of iterator is: {}> ({}, {})", counter, x1, y1);
            if x1 < x && x < x2 && y1 < y && y < y2 {
                return (x1 as i32, y1 as i32);
            }
        }
    }
    panic!("Square ID not found");
} 
3 Likes

I'll also note that the loops here seem really inefficient. It seems like you're just computing the smallest multiples of scalar that are less than x and y. You should be able to just divide x by scalar, round away any fractional part, and then multiply by scalar to get the correct value for x1. And again for y1.

fn get_clicked_square_id(x: f64, y: f64, scalar: usize) -> (i32, i32) {
    if x > 456.0 || y > 456.0 { panic!("No Square ID") }
    let scalar = scalar as f64;
    (
        (x.div_euclid(scalar) * scalar) as i32,
        (y.div_euclid(scalar) * scalar) as i32,
    )
} 
2 Likes

Why would you make this a panicking function instead of making it a fallible function that returns a Result<Point,Error>? This seems like trying to use panic in an entirely inappropriate way that runs counter to proper, idiomatic Rust. Were I code reviewing it in a work environment, I would reject it.

2 Likes

And if the input values of function should never trigger a panic due to the logic of surrounding code (i.e. if the panic would really be a bug), the more semantically meaningful might be to use unreachable!.

@gbutler69 Would you please show me what the code look like doing it the idiomatic way? It would be truly educational. A fine example of right and wrong.

EDIT: Improved version using a single range instead of literally twice if the "if" of the match arm.

I'd recommend something similar to what @skysch posted above, but, changing it to use Option instead of Panic. Something like this (Playground Link):

1 Like

@gbutler69
Wow! That is amazing code! Thank you so much!

2 Likes

Thank you so very much for your great help on efficiency...very good.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.