diff --git a/exercises/concept/magical-measurements/.docs/hints.md b/exercises/concept/magical-measurements/.docs/hints.md index e69de29b..d098df68 100644 --- a/exercises/concept/magical-measurements/.docs/hints.md +++ b/exercises/concept/magical-measurements/.docs/hints.md @@ -0,0 +1,34 @@ +# Hints + +## General + +- [The Cairo Book - Type Conversions][tcb-conversions] +- [The Cairo Book - Custom Type Conversion][tcb-ctc] + +## 1. Converting Signed to Unsigned + +- Check if the signed value is negative - if so, return `Option::None` +- Check if the signed value exceeds `u16::MAX` (65535) - if so, return `Option::None` +- For valid values, cast using `.try_into()` or direct conversion +- Remember that `u16` can hold values from 0 to 65535 + +## 2. Converting Unsigned to Signed + +- Since `u16::MAX` (65535) fits comfortably in `i32`, this conversion always succeeds +- Use `.into()` for safe conversion from `u16` to `i32` + +## 3. Custom Type Conversion + +- Implement the `Into` trait with the syntax: `impl IntoImplName of Into` +- In the `into` method, create a new instance of the target type +- Map the `potency` field from `Essence` to the `strength` field in `Elixir` +- Once the trait is implemented, you can use `.into()` on `Essence` instances + +## 4. Using the Into Trait + +- After implementing the `Into` trait, call `.into()` on the `Essence` parameter +- The compiler will automatically use your trait implementation +- The return type annotation helps Cairo know which conversion to use + +[tcb-conversions]: https://www.starknet.io/cairo-book/ch02-02-data-types.html#type-conversion +[tcb-ctc]: https://www.starknet.io/cairo-book/ch05-02-an-example-program-using-structs.html#conversions-of-custom-types diff --git a/exercises/concept/magical-measurements/.docs/introduction.md b/exercises/concept/magical-measurements/.docs/introduction.md index e69de29b..d7faa959 100644 --- a/exercises/concept/magical-measurements/.docs/introduction.md +++ b/exercises/concept/magical-measurements/.docs/introduction.md @@ -0,0 +1,68 @@ +# Introduction + +Type conversions in Cairo allow you to transform data from one type to another, enabling flexible and safe data manipulation. + +## Basics + +Cairo provides several mechanisms for type conversion: + +### Direct Casting + +For compatible numeric types, you can use direct casting: + +```rust +let small_number: u8 = 42; +let big_number: u32 = small_number.into(); +``` + +### The Into Trait + +The `Into` trait enables conversion from one type to another: + +```rust +let value: u16 = 100; +let converted: u32 = value.into(); +``` + +### The TryInto Trait + +For conversions that might fail, use `TryInto` which returns an `Option`: + +```rust +let large_value: u32 = 70000; +let small_value: Option = large_value.try_into(); + +match small_value { + Option::Some(v) => println!("Converted: {}", v), + Option::None => println!("Conversion failed"), +} +``` + +## Custom Type Conversions + +You can implement `Into` for your own types: + +```rust +#[derive(Drop)] +struct Celsius { + value: i32, +} + +#[derive(Drop)] +struct Fahrenheit { + value: i32, +} + +impl CelsiusIntoFahrenheit of Into { + fn into(self: Celsius) -> Fahrenheit { + Fahrenheit { value: (self.value * 9) / 5 + 32 } + } +} +``` + +## Safety Considerations + +- Use `TryInto` when conversion might fail (e.g., large to small types) +- Use `Into` when conversion always succeeds +- Always handle `Option::None` cases from `TryInto` +- Consider overflow and underflow when converting between signed and unsigned types diff --git a/exercises/concept/magical-measurements/.meta/config.json b/exercises/concept/magical-measurements/.meta/config.json index 616e633c..8195d451 100644 --- a/exercises/concept/magical-measurements/.meta/config.json +++ b/exercises/concept/magical-measurements/.meta/config.json @@ -1,6 +1,6 @@ { "authors": [ - "" + "0xNeshi" ], "files": { "solution": [ @@ -16,5 +16,5 @@ "Scarb.toml" ] }, - "blurb": "" + "blurb": "Learn Cairo's type conversion traits while helping Astrid the alchemist standardize magical measurements." } diff --git a/exercises/concept/magical-measurements/.meta/design.md b/exercises/concept/magical-measurements/.meta/design.md index e69de29b..7c19062f 100644 --- a/exercises/concept/magical-measurements/.meta/design.md +++ b/exercises/concept/magical-measurements/.meta/design.md @@ -0,0 +1,24 @@ +# Design + +## Learning objectives + +- Know what type conversions are. +- Know how to convert between signed and unsigned integers safely. +- Know how to implement the `Into` trait for custom types. +- Understand when conversions can fail and how to handle them with `Option`. + +## Out of scope + +- `TryFrom` trait implementations. +- Complex generic trait bounds. +- Advanced trait derivation. +- Performance implications of conversions. + +## Concepts + +- Type conversions + +## Prerequisites + +- Enums (Option) +- Pattern matching diff --git a/exercises/concept/magical-measurements/.meta/exemplar.cairo b/exercises/concept/magical-measurements/.meta/exemplar.cairo index e69de29b..f9524fac 100644 --- a/exercises/concept/magical-measurements/.meta/exemplar.cairo +++ b/exercises/concept/magical-measurements/.meta/exemplar.cairo @@ -0,0 +1,34 @@ +#[derive(Drop)] +pub struct Essence { + pub potency: u16, +} + +#[derive(Drop)] +pub struct Elixir { + pub strength: u16, +} + +pub fn convert_signed_to_unsigned(value: i32) -> Option { + if value < 0 { + Option::None + } else { + match value.try_into() { + Option::Some(v) => Option::Some(v), + Option::None => Option::None, + } + } +} + +pub fn convert_unsigned_to_signed(value: u16) -> i32 { + value.into() +} + +pub fn convert_essence_to_elixir(essence: Essence) -> Elixir { + essence.into() +} + +impl EssenceIntoElixir of Into { + fn into(self: Essence) -> Elixir { + Elixir { strength: self.potency } + } +} diff --git a/exercises/concept/magical-measurements/Scarb.toml b/exercises/concept/magical-measurements/Scarb.toml index e69de29b..dd0bec78 100644 --- a/exercises/concept/magical-measurements/Scarb.toml +++ b/exercises/concept/magical-measurements/Scarb.toml @@ -0,0 +1,7 @@ +[package] +name = "magical_measurements" +version = "0.1.0" +edition = "2024_07" + +[dev-dependencies] +cairo_test = "2.9.2" diff --git a/exercises/concept/magical-measurements/src/lib.cairo b/exercises/concept/magical-measurements/src/lib.cairo index e69de29b..7c7614c3 100644 --- a/exercises/concept/magical-measurements/src/lib.cairo +++ b/exercises/concept/magical-measurements/src/lib.cairo @@ -0,0 +1,37 @@ +/// Essence - a rare magical ingredient with potency +#[derive(Drop)] +pub struct Essence { + pub potency: u16, +} + +/// Elixir - a refined magical ingredient with strength +#[derive(Drop)] +pub struct Elixir { + pub strength: u16, +} + +/// Convert signed integer to unsigned integer (if possible) +pub fn convert_signed_to_unsigned(value: i32) -> Option { + // Convert i32 to u16 only if the value is positive and fits in u16 + panic!("implement `convert_signed_to_unsigned`") +} + +/// Convert unsigned integer to signed integer +pub fn convert_unsigned_to_signed(value: u16) -> i32 { + // Convert u16 to i32 (should always succeed) + panic!("implement `convert_unsigned_to_signed`") +} + +/// Convert Essence to Elixir using the Into trait +pub fn convert_essence_to_elixir(essence: Essence) -> Elixir { + // Use the Into trait to convert Essence to Elixir + panic!("implement `convert_essence_to_elixir`") +} + +// TODO: Implement the Into trait for Essence -> Elixir conversion +impl EssenceIntoElixir of Into { + fn into(self: Essence) -> Elixir { + panic!("implement `Into::::into`") + } +} + diff --git a/exercises/concept/magical-measurements/tests/magical_measurements.cairo b/exercises/concept/magical-measurements/tests/magical_measurements.cairo index e69de29b..a2e182c7 100644 --- a/exercises/concept/magical-measurements/tests/magical_measurements.cairo +++ b/exercises/concept/magical-measurements/tests/magical_measurements.cairo @@ -0,0 +1,94 @@ +use magical_measurements::{ + Essence, Elixir, convert_signed_to_unsigned, convert_unsigned_to_signed, + convert_essence_to_elixir, +}; + +#[test] +fn test_convert_positive_signed_to_unsigned() { + let result = convert_signed_to_unsigned(42); + assert!(result.is_some()); + assert_eq!(result.unwrap(), 42); +} + +#[test] +#[ignore] +fn test_convert_negative_signed_to_unsigned() { + let result = convert_signed_to_unsigned(-42); + assert!(result.is_none()); +} + +#[test] +#[ignore] +fn test_convert_zero_signed_to_unsigned() { + let result = convert_signed_to_unsigned(0); + assert!(result.is_some()); + assert_eq!(result.unwrap(), 0); +} + +#[test] +#[ignore] +fn test_convert_large_signed_to_unsigned() { + let result = convert_signed_to_unsigned(100000); // Exceeds u16::MAX (65535) + assert!(result.is_none()); +} + +#[test] +#[ignore] +fn test_convert_max_u16_signed_to_unsigned() { + let result = convert_signed_to_unsigned(65535); // u16::MAX + assert!(result.is_some()); + assert_eq!(result.unwrap(), 65535); +} + +#[test] +#[ignore] +fn test_convert_small_unsigned_to_signed() { + let result = convert_unsigned_to_signed(42); + assert_eq!(result, 42); +} + +#[test] +#[ignore] +fn test_convert_zero_unsigned_to_signed() { + let result = convert_unsigned_to_signed(0); + assert_eq!(result, 0); +} + +#[test] +#[ignore] +fn test_convert_max_unsigned_to_signed() { + let result = convert_unsigned_to_signed(65535); // u16::MAX + assert_eq!(result, 65535); +} + +#[test] +#[ignore] +fn test_convert_essence_to_elixir() { + let essence = Essence { potency: 100 }; + let elixir = convert_essence_to_elixir(essence); + assert_eq!(elixir.strength, 100); +} + +#[test] +#[ignore] +fn test_convert_essence_to_elixir_direct() { + let essence = Essence { potency: 250 }; + let elixir: Elixir = essence.into(); + assert_eq!(elixir.strength, 250); +} + +#[test] +#[ignore] +fn test_convert_essence_to_elixir_zero() { + let essence = Essence { potency: 0 }; + let elixir = convert_essence_to_elixir(essence); + assert_eq!(elixir.strength, 0); +} + +#[test] +#[ignore] +fn test_convert_essence_to_elixir_max() { + let essence = Essence { potency: 65535 }; + let elixir = convert_essence_to_elixir(essence); + assert_eq!(elixir.strength, 65535); +}