Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve get and get_mut method signature, Add From implementations for Vec<Vec<T>> and (Vec<T>, usize) #41

Merged
merged 7 commits into from
Dec 18, 2023

Conversation

pcbowers
Copy link
Contributor

@pcbowers pcbowers commented Dec 13, 2023

While using this library, I found it difficult to get neighbors because of the possibility of overflows when going negative. For instance, to get the neighbor that is up and to the left of the current cell, I would have to something like this:

let row: usize = 0;
let col: usize = 0;
if let Some(row) = row.checked_sub(1) {
    if let Some(col) = col.checked_sub(1) {
        if let Some(cell) = grid.get(row, col) {
            dbg!(cell);
        }
    }
}

This was a lot of code. Ideally, I could just do this:

let row: isize = 0;
let col: isize = 0;
if let Some(cell) = grid.get(row - 1, col - 1) {
    dbg!(cell);
}

Unfortunately, .get and .get_mut do not accept anything but usize, so this would cause an overflow warning.

I started adding this to my rust projects:

trait GetChecked<T> {
    fn get_checked(&self, row: isize, col: isize) -> Option<&T>;
    fn get_checked_mut(&mut self, row: isize, col: isize) -> Option<&mut T>;
}

impl<T> GetChecked<T> for Grid<T> {
    fn get_checked(&self, row: isize, col: isize) -> Option<&T> {
        self.get(row.try_into().ok()?, col.try_into().ok()?)
    }

    fn get_checked_mut(&mut self, row: isize, col: isize) -> Option<&mut T> {
        self.get_mut(row.try_into().ok()?, col.try_into().ok()?)
    }
}

This trait and implementation allowed me to add new methods called get_checked and get_checked_mut to do what I wanted: pass in an isize or any other thing that could be converted into a number. If it was less than 0, it would return None, which is perfect: there wouldn't be a value there anyway!

let row: isize = 1;
let col: isize = 1;
if let Some(cell) = grid.get_checked(row - 1, col - 1) {
    dbg!(cell);
}

After implementing this a couple times, I figured that it was useful enough to contribute to this library, hence this PR. (I also cleaned up some of the clippy linter warnings I was getting! And added implentation for From which makes more sense than adding a from_vec method, making it possible to potentially remove that function one day)

… that guarantees conversion into a usize works

This way get_unchecked and get_unchecked_mut are a little more versatile, but still won't randomly panic on you.
@pcbowers pcbowers changed the title Allow All values that implement TryInto<usize> as parameters to get and get_mut Improve get and get_mut method signature, Add From implementations for Vec<Vec<T>> and (Vec<T>, usize) Dec 13, 2023
@becheran
Copy link
Owner

Looks good to me and very useful. Had some mergers before this one which now causes merge conflicts. Could you please rebase to main? Then I will merge this as well.

@Einliterflasche
Copy link
Contributor

Einliterflasche commented Dec 16, 2023

I don't know whether this is intentional but I want to note that having a single generic type for both arguments (e.g. fo foo<U: impl Into<usize>(col: U, row: U)) prevents calling the function with two different types even if both implement Into<usize>. This could be allowed if we were to use seperate generic types, like this: fn get_checked(col: impl Into<usize>, row: impl Into<usize>)

@pcbowers
Copy link
Contributor Author

I don't know whether this is intentional but I want to note that having a single generic type for both arguments (e.g. fo foo<U: impl Into<usize>(col: U, row: U)) prevents calling the function with two different types even if both implement Into<usize>. This could be allowed if we were to use seperate generic types, like this: fn get_checked(col: impl Into<usize>, row: impl Into<usize>)

Good call! That was not intentional. I'll implement your suggestion when I rebase!

@pcbowers
Copy link
Contributor Author

Looks good to me and very useful. Had some mergers before this one which now causes merge conflicts. Could you please rebase to main? Then I will merge this as well.

@becheran, Should be ready to go! I realized I implemented a merge commit instead of a rebase (force of habit, sorry!). I can undo those commits if you feel strongly about rebasing.

@becheran becheran merged commit 8c07d46 into becheran:master Dec 18, 2023
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants