I wonder if I did this one the right way . the generics on the imp looks wierd to me.
// An imaginary magical school has a new report card generation system written in rust!
// Currently the system only supports creating report cards where the student's grade
// is represented numerically (e.g. 1.0 -> 5.5). However, the school also issues alphabetical grades
// (A+ -> F-) and needs to be able to print both types of report card!
// Make the necessary code changes to support alphabetical report cards, thereby making the second
// test pass.
// I AM NOT DONE
pub struct ReportCard<T> {
pub grade: T,
pub student_name: String,
pub student_age: u8,
}
impl <T: std::fmt::Display> ReportCard<T> {
pub fn print(&self) -> String {
format!("{} ({}) - achieved a grade of {}", &self.student_name, &self.student_age, &self.grade)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn generate_numeric_report_card() {
let report_card = ReportCard {
grade: 2.1,
student_name: "Tom Wriggle".to_string(),
student_age: 12,
};
assert_eq!(report_card.print(), "Tom Wriggle (12) - achieved a grade of 2.1");
}
#[test]
fn generate_alphabetic_report_card() {
// TODO: Make sure to change the grade here after you finish the exercise.
let report_card = ReportCard {
grade: "A+",
student_name: "Gary Plotter".to_string(),
student_age: 11,
};
assert_eq!(report_card.print(), "Gary Plotter (11) - achieved a grade of A+");
}
}
It’s a perfectly correct and acceptable solution. Everything I say below is fundamentally about style, so use your own judgement.
Usually, when the entire impl is going to be constrained like that, I’ll also include the constraint on the struct definition; there’s not much use in making an object that can’t do anything:
Right; the two options mean slightly different things, and different situations might call for each of them:
The first way prevents ReportCard from ever existing with a T that can’t be displayed.
The second always allows the ReportCard to exist, but only defines the print function if T can be displayed.
Your original solution is halfway in-between: any T can be used to create a ReportCard, but the all functions in the impl block will only be defined for displayable Ts. Personally, I usually find one of the other two options clearer.