Skip to content

Rework health statistics exercise #909

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

Merged
merged 3 commits into from
Jul 11, 2023

Conversation

fw-immunant
Copy link
Collaborator

No description provided.

weight may be a sensitive topic for some readers; use height instead as this isn't important to the content of the course
this lets us see a non-setter use case for &mut self

it also makes the 'statistics' side of this exercise more explicit as we count doctor visits
@djmitche djmitche self-requested a review July 6, 2023 17:20
Copy link
Collaborator

@djmitche djmitche left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good, thanks!

And, good call on weight -> height ❤️

@@ -2,6 +2,20 @@ pub struct User {
name: String,
age: u32,
height: f32,
doctor_visits: usize,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this intentionally different from HealthReport::visit_count?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes--a patient has visited the doctor a given number of times, but a health report corresponds to a particular visit whose count it carries. Maybe visit_index could be used, but I thought that might suggest zero-indexing or that there's a vector of visits somewhere.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plural ("visits") suggests a count. If it's not zero-indexed, what is it -- a datestamp?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably least confusing if we just make these variable names match. I'll update them to both be visit_count.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@domenukk, see above for your question about usize vs u32.

@djmitche djmitche merged commit 2f5dcba into google:main Jul 11, 2023
@michaellee8
Copy link

michaellee8 commented Jul 17, 2023

Is there a suggested solution on this exercise? Turns out I need to implement some not so neat approach to solve the Option<(u32, u32)> - (u32, u32) -> Option<(i32, i32)> problem https://gist.github.com/michaellee8/52a8a508f1dfa474b5b88675f23838dc. Is this the best approach one can get?

@djmitche
Copy link
Collaborator

There isn't a solution in the repo, but perhaps you'd like to add one?

This is one of a few questions in the exercise that always generates some interesting conversation that is, I think, instructive for students. A few ideas that have come up in these discussions:

  • Use i32 everywhere, which covers the range of conceivable blood pressures and differences between them
  • When calculating the difference, try_into can get unweildy. Using as is shorter and any undesirable wrapping can be avoided with some explicit checks.
  • Define BloodPressure and BloodPressureDifference types, with Sub implemented on BloodPressure returning a BloodPressureDifference. These types could constain their values to reasonable ranges and avoid the need for checks before subtracting.

This is an illustration of Rust's flexibility to meet the situation: as would be fine in a quick script, while custom types with stringent bounds checks probably make sense in a blood-pressure-monitoring application that will be seeking FDA approval.

@fw-immunant
Copy link
Collaborator Author

I would probably do something like this:

let blood_pressure_change = self.last_blood_pressure.map(|last| {
    let meas = measurements.blood_pressure;
    (meas.0 as i32 - last.0 as i32 , meas.1 as i32  - last.1 as i32)
});

However, this does handle overflow by implicitly panicking (in debug) wrapping (in release mode). I don't think there's a very sane behavior available for overflow here: any blood pressure greater than a few hundred is not a realistically possible measurement (much less medically possible) and should rather be caught at the construction of Measurements. This raises the design question of which errors to handle where (at measurement creation or report creation) and I think is a good way to inspire discussion about similar design questions for other Rust codebases.

@michaellee8
Copy link

There isn't a solution in the repo, but perhaps you'd like to add one?

This is one of a few questions in the exercise that always generates some interesting conversation that is, I think, instructive for students. A few ideas that have come up in these discussions:

  • Use i32 everywhere, which covers the range of conceivable blood pressures and differences between them
  • When calculating the difference, try_into can get unweildy. Using as is shorter and any undesirable wrapping can be avoided with some explicit checks.
  • Define BloodPressure and BloodPressureDifference types, with Sub implemented on BloodPressure returning a BloodPressureDifference. These types could constain their values to reasonable ranges and avoid the need for checks before subtracting.

This is an illustration of Rust's flexibility to meet the situation: as would be fine in a quick script, while custom types with stringent bounds checks probably make sense in a blood-pressure-monitoring application that will be seeking FDA approval.

I believe that it would have made much more sense to just use i32 since there are no way to have blood pressure exceeding the i32 range. I also agree thst a BloodPressure type would make sense in production code but given the way that the tests are written correctly a solution that uses a custom BloodPressure type will not pass the test. It leads to me having to write custom method to do the subtraction work since it is not possible to implement methods on built-in types like tuples.

@djmitche
Copy link
Collaborator

When I teach the course, I encourage students to not feel boxed-in by the existing code, any more than they would be boxed-in by existing code when writing a pull request. But, that is less clear from the contents of this repo.

yohcop pushed a commit to yohcop/comprehensive-rust that referenced this pull request Sep 12, 2023
* exercises: health-statistics: weight -> height

weight may be a sensitive topic for some readers; use height instead as this isn't important to the content of the course

* exercises: health-statistics: add health report

this lets us see a non-setter use case for &mut self

it also makes the 'statistics' side of this exercise more explicit as we count doctor visits

* exercises: health-statistics: normalize variable names
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.

4 participants