From d4d428ebaf31a3536eb3d8a75b33d0d2bab2af04 Mon Sep 17 00:00:00 2001 From: Nenad Date: Wed, 25 Dec 2024 20:01:38 +0100 Subject: [PATCH 1/8] Write the "Printing" concept (#323) * add design.md file * write about.md * write introduction.md * write introduction.md * format about.md * set config for printing * add links * add blurb * add hints.md * update tests * add missing prerequisites to config.json * Update hints headings * remove changes to the-realm-of-echoes --- concepts/printing/.meta/config.json | 4 +- concepts/printing/about.md | 120 ++++++++++++++++++++++++++++ concepts/printing/introduction.md | 5 ++ concepts/printing/links.json | 11 ++- config.json | 4 +- 5 files changed, 140 insertions(+), 4 deletions(-) diff --git a/concepts/printing/.meta/config.json b/concepts/printing/.meta/config.json index 35b1d32b..568525f6 100644 --- a/concepts/printing/.meta/config.json +++ b/concepts/printing/.meta/config.json @@ -1,7 +1,7 @@ { - "blurb": "", + "blurb": "Printing in Cairo allows you to display messages, debug information, or formatted values during program execution.", "authors": [ - "" + "0xNeshi" ], "contributors": [] } diff --git a/concepts/printing/about.md b/concepts/printing/about.md index bf63b082..b7a8f14e 100644 --- a/concepts/printing/about.md +++ b/concepts/printing/about.md @@ -1 +1,121 @@ # Printing + +Printing in Cairo allows you to display messages and values to the console, making it easier to debug programs or communicate information during execution. + +The printing system is both versatile and straightforward, providing tools to format and output data of various types. + +## Printing Basics + +Cairo provides two primary macros for printing text to the console: + +- **`println!`**: Prints output on a new line. +- **`print!`**: Prints output inline, without adding a new line. + +Both macros take a `ByteArray` as their primary argument, which can be a simple string or a formatted string with placeholders for values. + +```rust +// Printing a simple message +println!("Hello, Cairo!"); + +// Printing with values +let x = 10; +let y = 20; +println!("Values: x = {}, y = {}", x, y); +``` + +In the above example: + +- `{}` placeholders are replaced by the values of `x` and `y`. +- `println!` ensures the output ends with a new line. + +## Formatting Strings + +Sometimes, you might need to create a formatted string without immediately printing it. + +The `format!` macro allows you to do this. + +It works similarly to `println!` but returns a `ByteArray` instead of sending output to the console. + +```rust +let s1: ByteArray = "tic"; +let s2: ByteArray = "tac"; +let s3: ByteArray = "toe"; + +// Create a formatted string +let result = format!("{s1}-{s2}-{s3}"); + +println!("{}", result); // Output: tic-tac-toe +``` + +In this example: + +- `format!` combines `s1`, `s2`, and `s3` into a single string, separating them with dashes (`-`). +- The resulting string can be reused or printed later. + +## Printing Custom Data Types + +By default, Cairo can only print basic types like integers and strings. + +If you try to print a custom type, such as a struct, you'll encounter an error unless the type implements the `Display` or `Debug` traits. + +### Using `Display` for Custom Formatting + +The `Display` trait lets you define how a custom type should be printed. + +Here's an example: + +```rust +use core::fmt::{Display, Formatter, Error}; + +struct Point { + x: u8, + y: u8, +} + +impl PointDisplay of Display { + fn fmt(self: @Point, ref f: Formatter) -> Result<(), Error> { + write!(f, "Point({}, {})", *self.x, *self.y) + } +} + +let p = Point { x: 3, y: 4 }; +println!("{}", p); // Output: Point(3, 4) +``` + +In this example: + +- The `fmt` function defines how the `Point` struct is converted to a printable string. +- The `write!` macro appends the formatted string to the `Formatter`. + +### Debugging with `Debug` + +For debugging purposes, Cairo provides the `Debug` trait. + +You can derive it automatically for most types, making it quick and easy to print internal details of your structs. + +```rust +#[derive(Debug)] +struct Point { + x: u8, + y: u8, +} + +let p = Point { x: 3, y: 4 }; +println!("{:?}", p); // Output: Point { x: 3, y: 4 } +``` + +In this example: + +- `#[derive(Debug)]` automatically generates the implementation for the `Debug` trait. +- The `{:?}` placeholder tells `println!` to use the `Debug` trait for formatting. + +## Printing in Hexadecimal + +Cairo allows integers to be printed in hexadecimal format using the `{:x}` placeholder. + +```rust +let value: u32 = 255; +println!("{:x}", value); // Output: ff +``` + +This is especially useful when working with low-level data representations. diff --git a/concepts/printing/introduction.md b/concepts/printing/introduction.md index e10b99d0..2fc5c48f 100644 --- a/concepts/printing/introduction.md +++ b/concepts/printing/introduction.md @@ -1 +1,6 @@ # Introduction + +Printing in Cairo allows you to display messages, debug information, or formatted values during program execution. +With versatile macros like println! +and format!, you can output basic data types, custom formats, or even complex structures. +This makes it easy to inspect and communicate program behavior effectively. diff --git a/concepts/printing/links.json b/concepts/printing/links.json index fe51488c..63f8d70f 100644 --- a/concepts/printing/links.json +++ b/concepts/printing/links.json @@ -1 +1,10 @@ -[] +[ + { + "url": "https://book.cairo-lang.org/ch11-08-printing.html", + "description": "Printing in The Cairo book" + }, + { + "url": "https://book.cairo-lang.org/appendix-03-derivable-traits.html#debug-for-printing-and-debugging", + "description": "Derivable Debug Trait in The Cairo Book" + } +] diff --git a/config.json b/config.json index 574ec5e8..8f54bf8c 100644 --- a/config.json +++ b/config.json @@ -205,7 +205,9 @@ "printing" ], "prerequisites": [ - "traits" + "strings", + "traits", + "structs" ], "status": "wip" }, From 988231e8b35a0b19dbdf5fe6f673fee0093d2de2 Mon Sep 17 00:00:00 2001 From: Nenad Date: Wed, 25 Dec 2024 20:02:12 +0100 Subject: [PATCH 2/8] Add Concept Exercise Metadata files: the-realm-of-echoes (#326) --- .../the-realm-of-echoes/.docs/hints.md | 27 ++++++ .../the-realm-of-echoes/.docs/introduction.md | 46 ++++++++++ .../the-realm-of-echoes/.meta/config.json | 2 +- .../the-realm-of-echoes/.meta/design.md | 36 ++++++++ .../tests/the_realm_of_echoes.cairo | 83 +++++++++++++++++-- 5 files changed, 184 insertions(+), 10 deletions(-) diff --git a/exercises/concept/the-realm-of-echoes/.docs/hints.md b/exercises/concept/the-realm-of-echoes/.docs/hints.md index e69de29b..9e5c7101 100644 --- a/exercises/concept/the-realm-of-echoes/.docs/hints.md +++ b/exercises/concept/the-realm-of-echoes/.docs/hints.md @@ -0,0 +1,27 @@ +# Hints + +## General + +- [The Cairo Book: ByteArrays][book-bytearrays] +- [Starknet By Example: Strings & ByteArrays][sbe-strings] + +### 1. Format a magical chant + +- Use the `format!` macro to combine multiple `ByteArray` inputs into a single string. +- Separate the chants with a delimiter such as `"-"` to match the expected output. +- Ensure the output is returned as a `ByteArray`. + +### 2. Implement a `Display` trait for `EchoStone` + +- The `Display` trait allows you to define how the `EchoStone` should appear when printed with `{}`. +- Use the `write!` macro within the `fmt` function to format the output string. +- Include both `power` and `duration` fields in a clear and concise format, e.g., `EchoStone [power: X, duration: Y]`. + +### 3. Implement a `Debug` trait for `EchoStone` + +- The `Debug` trait is used for debugging purposes, allowing for more detailed or structured output when printed with `{:?}`. +- Follow a clear debugging format, such as `Debugging EchoStone: { power: X, duration: Y }`. +- Use the `write!` macro to create the debug output. + +[book-bytearrays]: https://book.cairo-lang.org/ch02-02-data-types.html#string-types +[sbe-strings]: https://starknet-by-example.voyager.online/getting-started/basics/bytearrays-strings diff --git a/exercises/concept/the-realm-of-echoes/.docs/introduction.md b/exercises/concept/the-realm-of-echoes/.docs/introduction.md index e69de29b..defc29ef 100644 --- a/exercises/concept/the-realm-of-echoes/.docs/introduction.md +++ b/exercises/concept/the-realm-of-echoes/.docs/introduction.md @@ -0,0 +1,46 @@ +# Introduction + +Printing in Cairo allows you to display messages or debug information during program execution. + +## Basics + +Cairo provides two macros for printing: + +- `println!`: Outputs a message followed by a newline. +- `print!`: Outputs a message without a newline. + +```rust +println!("Hello, Cairo!"); +println!("x = {}, y = {}", 10, 20); +``` + +Placeholders `{}` are replaced with provided values. + +## Formatting Strings + +Use `format!` to create a `ByteArray` without immediately printing: + +```rust +let result = format!("{}-{}-{}", "tic", "tac", "toe"); +println!("{}", result); // Output: tic-tac-toe +``` + +## Custom Data Types + +For custom types, implement `Display` or derive `Debug` for printing: + +```rust +#[derive(Debug)] +struct Point { x: u8, y: u8 } + +let p = Point { x: 3, y: 4 }; +println!("{:?}", p); // Debug output: Point { x: 3, y: 4 } +``` + +## Hexadecimal Printing + +Use `{:x}` to print integers as hexadecimal: + +```rust +println!("{:x}", 255); // Output: ff +``` diff --git a/exercises/concept/the-realm-of-echoes/.meta/config.json b/exercises/concept/the-realm-of-echoes/.meta/config.json index 8af744c3..83352bc6 100644 --- a/exercises/concept/the-realm-of-echoes/.meta/config.json +++ b/exercises/concept/the-realm-of-echoes/.meta/config.json @@ -16,5 +16,5 @@ "Scarb.toml" ] }, - "blurb": "" + "blurb": "Learn printing and formatting values with the art of EchoSpeak." } diff --git a/exercises/concept/the-realm-of-echoes/.meta/design.md b/exercises/concept/the-realm-of-echoes/.meta/design.md index e69de29b..4737e9f1 100644 --- a/exercises/concept/the-realm-of-echoes/.meta/design.md +++ b/exercises/concept/the-realm-of-echoes/.meta/design.md @@ -0,0 +1,36 @@ +# Design + +## Goal + +Introduce students to the use of formatting traits like `Display` and `Debug` and how to concatenate strings effectively using the `format!` macro. + +## Learning Objectives + +- Understand and implement the `Display` trait for custom types. +- Understand and implement the `Debug` trait for custom types. +- Learn how to use the `format!` macro to concatenate strings and format output. +- Explore the differences between the `Display` and `Debug` traits in formatting. + +## Out of Scope + +- Advanced string manipulation techniques. +- Error handling mechanisms beyond basic usage. +- Interaction with non-primitive custom types within formatting. + +## Concepts + +- printing + +## Prerequisites + +- traits +- strings +- structs + +## Resources to Refer To + +- [Cairo Book - Printing][printing] +- [Cairo Book - Macros][macros] + +[printing]: https://book.cairo-lang.org/ch11-08-printing.html +[macros]: https://book.cairo-lang.org/ch11-05-macros.html diff --git a/exercises/concept/the-realm-of-echoes/tests/the_realm_of_echoes.cairo b/exercises/concept/the-realm-of-echoes/tests/the_realm_of_echoes.cairo index 69e31eab..95601ebc 100644 --- a/exercises/concept/the-realm-of-echoes/tests/the_realm_of_echoes.cairo +++ b/exercises/concept/the-realm-of-echoes/tests/the_realm_of_echoes.cairo @@ -1,21 +1,86 @@ use the_realm_of_echoes::{EchoStone, format_magical_chant}; +const U32_MAX: u32 = 0xFFFFFFFF; + +#[test] +fn format_magical_chant_basic() { + let chant1 = "abra"; + let chant2 = "cadabra"; + let chant3 = "alakazam"; + assert_eq!(format_magical_chant(chant1, chant2, chant3), "abra-cadabra-alakazam"); +} + +#[test] +#[ignore] +fn format_magical_chant_empty_strings() { + let chant1 = ""; + let chant2 = ""; + let chant3 = ""; + assert_eq!(format_magical_chant(chant1, chant2, chant3), "--"); +} + +#[test] +#[ignore] +fn format_magical_chant_mixed_empty_and_non_empty() { + let chant1 = "abra"; + let chant2 = ""; + let chant3 = "alakazam"; + assert_eq!(format_magical_chant(chant1, chant2, chant3), "abra--alakazam"); +} + +#[test] +#[ignore] +fn format_magical_chant_with_special_characters() { + let chant1 = "ab@ra"; + let chant2 = "ca!dabra"; + let chant3 = "ala#kazam"; + assert_eq!(format_magical_chant(chant1, chant2, chant3), "ab@ra-ca!dabra-ala#kazam"); +} + +#[test] +#[ignore] +fn echo_stone_display_basic() { + let stone = EchoStone { power: 100, duration: 50 }; + assert_eq!(format!("{stone}"), "EchoStone [power: 100, duration: 50]"); +} + #[test] -fn test_format_magical_chant() { - let result = format_magical_chant("Spark", "Shine", "Glow"); - assert_eq!(result, "Spark-Shine-Glow"); +#[ignore] +fn echo_stone_display_zero_values() { + let stone = EchoStone { power: 0, duration: 0 }; + assert_eq!(format!("{stone}"), "EchoStone [power: 0, duration: 0]"); +} + + +#[test] +#[ignore] +fn echo_stone_display_large_values() { + let stone = EchoStone { power: U32_MAX, duration: U32_MAX }; + assert_eq!( + format!("{stone}"), format!("EchoStone [power: {}, duration: {}]", U32_MAX, U32_MAX), + ); +} + +#[test] +#[ignore] +fn echo_stone_debug_basic() { + let stone = EchoStone { power: 100, duration: 50 }; + assert_eq!(format!("{stone:?}"), "Debugging EchoStone: { power: 100, duration: 50 }"); } #[test] #[ignore] -fn test_stringify_echo_stone() { - let stone = EchoStone { power: 100, duration: 300 }; - assert_eq!(format!("{stone}"), "EchoStone [power: 100, duration: 300]"); +fn echo_stone_debug_zero_values() { + let stone = EchoStone { power: 0, duration: 0 }; + assert_eq!(format!("{stone:?}"), "Debugging EchoStone: { power: 0, duration: 0 }"); } #[test] #[ignore] -fn test_debug_echo_stone() { - let stone = EchoStone { power: 100, duration: 300 }; - assert_eq!(format!("{:?}", stone), "Debugging EchoStone: { power: 100, duration: 300 }"); +fn echo_stone_debug_large_values() { + let stone = EchoStone { power: U32_MAX, duration: U32_MAX }; + assert_eq!( + format!("{stone:?}"), + format!("Debugging EchoStone: {{ power: {}, duration: {} }}", U32_MAX, U32_MAX), + ); } From 42cdcfaff27e5de6ccc52bf4433065489ddf4d9d Mon Sep 17 00:00:00 2001 From: Nenad Date: Sun, 29 Dec 2024 19:27:15 +0100 Subject: [PATCH 3/8] implement type-conversion concept (#328) --- concepts/type-conversion/.meta/config.json | 6 +- concepts/type-conversion/about.md | 73 ++++++++++++++++++++++ concepts/type-conversion/introduction.md | 4 ++ concepts/type-conversion/links.json | 11 +++- 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/concepts/type-conversion/.meta/config.json b/concepts/type-conversion/.meta/config.json index 35b1d32b..2a471bcc 100644 --- a/concepts/type-conversion/.meta/config.json +++ b/concepts/type-conversion/.meta/config.json @@ -1,7 +1,5 @@ { - "blurb": "", - "authors": [ - "" - ], + "blurb": "Type conversion in Cairo allows you to transform values between different types using the `Into` and `TryInto` traits.", + "authors": ["0xNeshi"], "contributors": [] } diff --git a/concepts/type-conversion/about.md b/concepts/type-conversion/about.md index 7b930e51..58f70b64 100644 --- a/concepts/type-conversion/about.md +++ b/concepts/type-conversion/about.md @@ -1 +1,74 @@ # Type Conversion + +Cairo supports type conversion through the `Into` and `TryInto` traits from the core library, commonly used for in-built and custom types. + +These traits define methods for converting values between types: + +- **`Into`**: Used for guaranteed, infallible conversions. +- **`TryInto`**: Used for fallible conversions, returning `Option`. + +## Using `Into` + +`Into` handles conversions where the target type can always accommodate the source value. + +```rust +fn main() { + let my_u8: u8 = 10; + let my_felt252: felt252 = my_u8.into(); // Infallible conversion + let my_u256: u256 = my_felt252.into(); // Conversion to larger types +} +``` + +## Using `TryInto` + +`TryInto` is for cases where the conversion might fail, returning `Option`. + +```rust +fn main() { + let my_u256: u256 = 10; + let my_felt252: felt252 = my_u256.try_into().unwrap(); // Success + let my_large_u16: u16 = 2048; + let my_large_u8: u8 = my_large_u16.try_into().unwrap(); // Panics because value doesn't fit in `u8` +} +``` + +## Custom Type Conversion + +You can implement these traits for your own types. + +### Example: `Into` + +```rust +#[derive(Drop)] +struct Rectangle { + width: u64, + height: u64, +} + +#[derive(Drop)] +struct Square { + side_length: u64, +} + +impl SquareIntoRectangle of Into { + fn into(self: Square) -> Rectangle { + Rectangle { width: self.side_length, height: self.side_length } + } +} +``` + +### Example: `TryInto` + +```rust +impl RectangleIntoSquare of TryInto { + fn try_into(self: Rectangle) -> Option { + if self.height == self.width { + Option::Some(Square { side_length: self.height }) + } else { + Option::None + } + } +} +``` + +This setup provides flexible and type-safe conversions for both built-in and custom types in Cairo. diff --git a/concepts/type-conversion/introduction.md b/concepts/type-conversion/introduction.md index e10b99d0..150e8ed5 100644 --- a/concepts/type-conversion/introduction.md +++ b/concepts/type-conversion/introduction.md @@ -1 +1,5 @@ # Introduction + +Type conversion in Cairo allows you to transform values between different types using the `Into` and `TryInto` traits. +The `Into` trait is used for guaranteed conversions, while `TryInto` handles fallible conversions that may fail if the value doesn't fit the target type. +These traits can also be implemented for custom types, enabling flexible and precise conversions in your programs. diff --git a/concepts/type-conversion/links.json b/concepts/type-conversion/links.json index fe51488c..919f905c 100644 --- a/concepts/type-conversion/links.json +++ b/concepts/type-conversion/links.json @@ -1 +1,10 @@ -[] +[ + { + "url": "https://book.cairo-lang.org/ch02-02-data-types.html#type-conversion", + "description": "Type Conversion in The Cairo Book" + }, + { + "url": "https://book.cairo-lang.org/ch05-02-an-example-program-using-structs.html#conversions-of-custom-types", + "description": "Conversions of Custom Types in The Cairo Book" + } +] From 418098847ad5ff8566132c3f331cc2b6e64fc1c4 Mon Sep 17 00:00:00 2001 From: Nenad Date: Mon, 30 Dec 2024 08:08:45 +0100 Subject: [PATCH 4/8] Add the `chrono-realms-chrono-chain` Exercise (#322) * add introduction.md * update descr in control-flow links * add missing comma in control-flow blurb * Add smart pointers concept * rewrite introduction.md * add hints * write blurb * format md files * add design * update link in hints * add more tests + update to be trait-based * Update config.json prerequisites * delete redundant whitespace in links * ignore all non-first tests * remove redundant comments from tests * turn time-tree into chrono-chain * remove test_ from test names * remove changes to concepts --- config.json | 9 +- .../chrono-realms-chrono-chain/.docs/hints.md | 18 ++ .../.docs/instructions.md | 46 ++++ .../.docs/introduction.md | 52 ++++ .../.meta/config.json | 4 +- .../.meta/design.md | 36 +++ .../.meta/exemplar.cairo | 30 +++ .../Scarb.toml | 2 +- .../chrono-realms-chrono-chain/src/lib.cairo | 19 ++ .../tests/chrono_realms_chrono_chain.cairo | 230 ++++++++++++++++++ .../chrono-realms-time-tree/.docs/hints.md | 0 .../.docs/instructions.md | 46 ---- .../.docs/introduction.md | 0 .../chrono-realms-time-tree/.meta/design.md | 0 .../.meta/exemplar.cairo | 27 -- .../chrono-realms-time-tree/src/lib.cairo | 16 -- .../tests/chrono_realms_time_tree.cairo | 38 --- 17 files changed, 439 insertions(+), 134 deletions(-) create mode 100644 exercises/concept/chrono-realms-chrono-chain/.docs/hints.md create mode 100644 exercises/concept/chrono-realms-chrono-chain/.docs/instructions.md create mode 100644 exercises/concept/chrono-realms-chrono-chain/.docs/introduction.md rename exercises/concept/{chrono-realms-time-tree => chrono-realms-chrono-chain}/.meta/config.json (65%) create mode 100644 exercises/concept/chrono-realms-chrono-chain/.meta/design.md create mode 100644 exercises/concept/chrono-realms-chrono-chain/.meta/exemplar.cairo rename exercises/concept/{chrono-realms-time-tree => chrono-realms-chrono-chain}/Scarb.toml (71%) create mode 100644 exercises/concept/chrono-realms-chrono-chain/src/lib.cairo create mode 100644 exercises/concept/chrono-realms-chrono-chain/tests/chrono_realms_chrono_chain.cairo delete mode 100644 exercises/concept/chrono-realms-time-tree/.docs/hints.md delete mode 100644 exercises/concept/chrono-realms-time-tree/.docs/instructions.md delete mode 100644 exercises/concept/chrono-realms-time-tree/.docs/introduction.md delete mode 100644 exercises/concept/chrono-realms-time-tree/.meta/design.md delete mode 100644 exercises/concept/chrono-realms-time-tree/.meta/exemplar.cairo delete mode 100644 exercises/concept/chrono-realms-time-tree/src/lib.cairo delete mode 100644 exercises/concept/chrono-realms-time-tree/tests/chrono_realms_time_tree.cairo diff --git a/config.json b/config.json index 8f54bf8c..a6183bd3 100644 --- a/config.json +++ b/config.json @@ -263,14 +263,15 @@ "status": "wip" }, { - "slug": "chrono-realms-time-tree", - "name": "Chrono Realms Time Tree", - "uuid": "a72139a1-825e-4349-a6b5-400b665fbe07", + "slug": "chrono-realms-chrono-chain", + "name": "Chrono Realms Chrono Chain", + "uuid": "e04f808a-25dc-4448-bc45-d3da0db9819a", "concepts": [ "smart-pointers" ], "prerequisites": [ - "structs" + "traits", + "enums" ], "status": "wip" } diff --git a/exercises/concept/chrono-realms-chrono-chain/.docs/hints.md b/exercises/concept/chrono-realms-chrono-chain/.docs/hints.md new file mode 100644 index 00000000..d4fcf74e --- /dev/null +++ b/exercises/concept/chrono-realms-chrono-chain/.docs/hints.md @@ -0,0 +1,18 @@ +# Hints + +## General + +- [The Cairo Book: Smart Pointers][smart-pointers] + +## 1. Build the ChronoChain from an array of `u32` values + +- We can iterate through the array and construct a chain in reverse, starting from the `End` and linking each value with the `Link` variant. +- Consider using `span.pop_back()` to iterate through the array in reverse order, this will help us create the chain from the end to the start. + +## 2. Sum the values in the ChronoChain + +- Use pattern matching to handle the two variants of `ChronoChain`. +- When you reach `End`, the sum is 0, and when you reach `Link`, you add the value and recursively sum the rest of the chain. +- This approach is similar to a recursive function that traverses the structure until it reaches the base case (`End`). + +[smart-pointers]: https://book.cairo-lang.org/ch11-02-smart-pointers.html diff --git a/exercises/concept/chrono-realms-chrono-chain/.docs/instructions.md b/exercises/concept/chrono-realms-chrono-chain/.docs/instructions.md new file mode 100644 index 00000000..0201d2c8 --- /dev/null +++ b/exercises/concept/chrono-realms-chrono-chain/.docs/instructions.md @@ -0,0 +1,46 @@ +# Instructions + +In **Chrono Realms**, Time Keepers often deal with not just trees of timelines, but **Chrono Chains**-sequences of linked **TimeNodes**, each representing a specific moment in time. +A **Chrono Chain** is a straight path of sequential moments, where each **TimeNode** connects to the next. +These **Chrono Chains** are useful when traveling through a series of specific events, as they allow Time Keepers to follow a single timeline. + +However, to handle these potentially long **Chrono Chains**, Time Keepers use **Smart Pointers (Box)** to safely manage and traverse these lists of moments without causing unnecessary memory duplication or overflow. +Each **TimeNode** holds a reference to the next node, forming a recursive structure. + +Your task as an apprentice is to implement a **Chrono Chain** as a recursive list structure using smart pointers. + +In this exercise, you will: + +1. Create a recursive `ChronoChain` enum, representing a list of moments. +2. Use the `Box` smart pointer to store the recursive nodes. +3. Implement a function to create a `ChronoChain` from an array of `u32` values. +4. Implement a function to traverse the `ChronoChain` and sum up the values stored in the list. + +## 1. Define the Recursive `ChronoChain` Enum + +Create a recursive enum `ChronoChain` with two variants: + +- `End`: Represents the end of the list. +- `Link`: Contains a `u32` value and a boxed reference to the next node in the chain. + +## 2. Create a Function to Build a ChronoChain + +Write a function `ChronoChain::build` that takes an array of `u32` values and returns a `ChronoChain`, linking the values sequentially using smart pointers. + +## 3. Implement the Sum Function + +Write a function `ChronoChain::sum` to recursively traverse the `ChronoChain` and sum the values of all nodes. + +## Example Usage + +```rust +fn main() { + // Create a ChronoChain from an array of values + let chrono_chain = ChronoChain::build(array![10, 20, 30]); + + // Sum the values in the ChronoChain + let total_sum = chrono_chain.sum(); + + println!("Total Time Power: {}", total_sum); +} +``` diff --git a/exercises/concept/chrono-realms-chrono-chain/.docs/introduction.md b/exercises/concept/chrono-realms-chrono-chain/.docs/introduction.md new file mode 100644 index 00000000..880a6084 --- /dev/null +++ b/exercises/concept/chrono-realms-chrono-chain/.docs/introduction.md @@ -0,0 +1,52 @@ +# Introduction + +Smart pointers in Cairo are advanced data structures that ensure safe and efficient memory management by adding safety features to regular pointers, preventing common issues like dereferencing null pointers or accessing uninitialized memory. + +## What is a Smart Pointer? + +A smart pointer behaves like a regular pointer but tracks ownership and ensures safe memory access, preventing issues like null or dangling pointer dereferencing. + +## Types of Smart Pointers + +Cairo provides several smart pointer types, such as `Box` and `Nullable`: + +- **`Box`**: Stores data in a special memory segment, ideal for large or dynamically-sized data. + It allows transferring ownership without copying the data. +- **`Nullable`**: Points to either a valid value of type `T` or `null`, useful for handling optional values. + +## Memory Safety + +Smart pointers help prevent unsafe memory access, ensuring memory is automatically deallocated when no longer needed, thus reducing the risk of memory leaks. + +### Example: Using `Box` for Recursive Types + +Smart pointers like `Box` allow for safe handling of recursive types, such as in a binary tree, by allocating memory efficiently and avoiding infinite recursion. + +```rust +use core::box::{BoxTrait}; + +#[derive(Copy, Drop)] +enum BinaryTree { + Leaf: u32, + Node: (u32, Box, Box), +} + +fn main() { + let leaf1 = BinaryTree::Leaf(1); + let leaf2 = BinaryTree::Leaf(2); + let node = BinaryTree::Node((3, BoxTrait::new(leaf1), BoxTrait::new(leaf2))); + println!("{:?}", node); +} +``` + +## Performance Benefits + +Smart pointers improve performance by passing references to data instead of copying large structures, reducing memory overhead. + +```rust +// `Cart` is a large struct that contains a lot of information +fn pass_pointer(cart: Box) { + let cart = cart.unbox(); + println!("{} is shopping today and bought {} items", cart.buyer, cart.items); +} +``` diff --git a/exercises/concept/chrono-realms-time-tree/.meta/config.json b/exercises/concept/chrono-realms-chrono-chain/.meta/config.json similarity index 65% rename from exercises/concept/chrono-realms-time-tree/.meta/config.json rename to exercises/concept/chrono-realms-chrono-chain/.meta/config.json index edd96af6..8d9e967a 100644 --- a/exercises/concept/chrono-realms-time-tree/.meta/config.json +++ b/exercises/concept/chrono-realms-chrono-chain/.meta/config.json @@ -7,7 +7,7 @@ "src/lib.cairo" ], "test": [ - "tests/chrono_realms_time_tree.cairo" + "tests/chrono_realms_chrono_chain.cairo" ], "exemplar": [ ".meta/exemplar.cairo" @@ -16,5 +16,5 @@ "Scarb.toml" ] }, - "blurb": "" + "blurb": "Learn smart pointers by building the magical Chrono Chain" } diff --git a/exercises/concept/chrono-realms-chrono-chain/.meta/design.md b/exercises/concept/chrono-realms-chrono-chain/.meta/design.md new file mode 100644 index 00000000..bdb2a7a2 --- /dev/null +++ b/exercises/concept/chrono-realms-chrono-chain/.meta/design.md @@ -0,0 +1,36 @@ +# Design + +## Goal + +Introduce the student to working with recursive types and smart pointers in Cairo. + +## Learning objectives + +- Understand how to define and use recursive types with enums in Cairo. +- Learn how to use smart pointers (`Box`) for handling recursive data structures. +- Practice working with recursive functions to traverse and manipulate linked data. + +## Out of scope + +- Advanced memory management concepts related to smart pointers beyond `Box`. +- Deep dive into optimization of recursive data structures. + +## Concepts + +- Enums +- Recursive types +- Smart pointers (`Box`) + +## Prerequisites + +- Traits +- Basic understanding of enums and data types in Cairo. +- Familiarity with smart pointers in Cairo. + +## Resources to refer to + +- [Cairo Book - The Box Type][box] +- [Cairo Book - Enums][enums] + +[box]: https://book.cairo-lang.org/ch11-02-smart-pointers.html#the-boxt-type-to-manipulate-pointers +[enums]: https://book.cairo-lang.org/ch06-01-enums.html diff --git a/exercises/concept/chrono-realms-chrono-chain/.meta/exemplar.cairo b/exercises/concept/chrono-realms-chrono-chain/.meta/exemplar.cairo new file mode 100644 index 00000000..a1a0cecc --- /dev/null +++ b/exercises/concept/chrono-realms-chrono-chain/.meta/exemplar.cairo @@ -0,0 +1,30 @@ +// Define the recursive ChronoChain enum +#[derive(Copy, Drop)] +pub enum ChronoChain { + End, + Link: (u32, Box), +} + +#[generate_trait] +pub impl ChronoChainImpl of ChronoChainTrait { + // Function to build a ChronoChain from an array of u32 values + fn build(arr: Array) -> ChronoChain { + let mut chain = ChronoChain::End; + + // Iterate in reverse to build the chain from the end to the beginning + let mut span = arr.span(); + while let Option::Some(value) = span.pop_back() { + chain = ChronoChain::Link((*value, BoxTrait::new(chain))); + }; + + chain + } + + // Function to sum the values in the ChronoChain + fn sum(self: ChronoChain) -> u64 { + match self { + ChronoChain::End => 0, + ChronoChain::Link((value, next)) => value.into() + next.unbox().sum(), + } + } +} diff --git a/exercises/concept/chrono-realms-time-tree/Scarb.toml b/exercises/concept/chrono-realms-chrono-chain/Scarb.toml similarity index 71% rename from exercises/concept/chrono-realms-time-tree/Scarb.toml rename to exercises/concept/chrono-realms-chrono-chain/Scarb.toml index 8865ffcb..e5af8176 100644 --- a/exercises/concept/chrono-realms-time-tree/Scarb.toml +++ b/exercises/concept/chrono-realms-chrono-chain/Scarb.toml @@ -1,5 +1,5 @@ [package] -name = "chrono_realms_time_tree" +name = "chrono_realms_chrono_chain" version = "0.1.0" edition = "2024_07" diff --git a/exercises/concept/chrono-realms-chrono-chain/src/lib.cairo b/exercises/concept/chrono-realms-chrono-chain/src/lib.cairo new file mode 100644 index 00000000..6d3b5410 --- /dev/null +++ b/exercises/concept/chrono-realms-chrono-chain/src/lib.cairo @@ -0,0 +1,19 @@ +// Define the recursive ChronoChain enum +#[derive(Copy, Drop)] +pub enum ChronoChain { + End, + Link: (u32, Box), +} + +#[generate_trait] +pub impl ChronoChainImpl of ChronoChainTrait { + // Function to build a ChronoChain from an array of u32 values + fn build(arr: Array) -> ChronoChain { + panic!("implement `ChronoChain::build`") + } + + // Function to sum the values in the ChronoChain + fn sum(self: ChronoChain) -> u64 { + panic!("implement `ChronoChain::sum`") + } +} diff --git a/exercises/concept/chrono-realms-chrono-chain/tests/chrono_realms_chrono_chain.cairo b/exercises/concept/chrono-realms-chrono-chain/tests/chrono_realms_chrono_chain.cairo new file mode 100644 index 00000000..5ccb740a --- /dev/null +++ b/exercises/concept/chrono-realms-chrono-chain/tests/chrono_realms_chrono_chain.cairo @@ -0,0 +1,230 @@ +use chrono_realms_chrono_chain::{ChronoChainTrait, ChronoChain}; + +const U32_MAX: u32 = 0xFFFFFFFF; + +#[test] +fn build_empty_array() { + let chrono_chain = ChronoChainTrait::build(array![]); + + match chrono_chain { + ChronoChain::End => (), + _ => panic!("Expected ChronoChain to be End for an empty array"), + } +} + +#[test] +#[ignore] +fn build_single_element_array() { + let chrono_chain = ChronoChainTrait::build(array![42]); + + match chrono_chain { + ChronoChain::Link(( + value, next, + )) => { + assert_eq!(value, 42); + match next.unbox() { + ChronoChain::End => (), + _ => panic!("Expected next to be End"), + } + }, + _ => panic!("Expected ChronoChain to be Link for a single element array"), + } +} + +#[test] +#[ignore] +fn build_multiple_elements_array() { + let chrono_chain = ChronoChainTrait::build(array![1, 2, 3]); + + match chrono_chain { + ChronoChain::Link(( + value, next, + )) => { + assert_eq!(value, 1); + match next.unbox() { + ChronoChain::Link(( + value, next, + )) => { + assert_eq!(value, 2); + match next.unbox() { + ChronoChain::Link(( + value, next, + )) => { + assert_eq!(value, 3); + match next.unbox() { + ChronoChain::End => (), + _ => panic!("Expected next to be End after the last link"), + } + }, + _ => panic!("Expected second link to be Link"), + } + }, + _ => panic!("Expected third link to be Link"), + } + }, + _ => panic!("Expected ChronoChain to be Link for multiple elements"), + } +} + +#[test] +#[ignore] +fn build_duplicate_values_array() { + let chrono_chain = ChronoChainTrait::build(array![5, 5, 5]); + + match chrono_chain { + ChronoChain::Link(( + value, next, + )) => { + assert_eq!(value, 5); + match next.unbox() { + ChronoChain::Link(( + value, next, + )) => { + assert_eq!(value, 5); + match next.unbox() { + ChronoChain::Link(( + value, next, + )) => { + assert_eq!(value, 5); + match next.unbox() { + ChronoChain::End => (), + _ => panic!("Expected next to be End after the last link"), + } + }, + _ => panic!("Expected third link to be Link"), + } + }, + _ => panic!("Expected second link to be Link"), + } + }, + _ => panic!("Expected ChronoChain to be Link for duplicate values"), + } +} + + +#[test] +#[ignore] +fn build_large_values_array() { + let chrono_chain = ChronoChainTrait::build(array![U32_MAX, U32_MAX]); + + match chrono_chain { + ChronoChain::Link(( + value, next, + )) => { + assert_eq!(value, U32_MAX); + match next.unbox() { + ChronoChain::Link(( + value, next, + )) => { + assert_eq!(value, U32_MAX); + match next.unbox() { + ChronoChain::End => (), + _ => panic!("Expected next to be End after the last link"), + } + }, + _ => panic!("Expected second link to be Link"), + } + }, + _ => panic!("Expected ChronoChain to be Link for large values"), + } +} + +#[test] +#[ignore] +fn build_large_array() { + let mut chrono_chain = ChronoChainTrait::build(array_in_range((1..1000))); + + let mut count = 1; + while let ChronoChain::Link((value, next)) = chrono_chain { + assert_eq!(value, count); + count += 1; + chrono_chain = next.unbox(); + }; + + match chrono_chain { + ChronoChain::End => (), + _ => panic!("Expected ChronoChain to be End at the end of the chain"), + } +} + +#[test] +#[ignore] +fn sum_chain() { + let chrono_chain = ChronoChainTrait::build(array![1, 2, 3]); + + let sum = chrono_chain.sum(); + assert_eq!(sum, 6); +} + +#[test] +#[ignore] +fn sum_empty_chain() { + let chrono_chain = ChronoChainTrait::build(array![]); + + let sum = chrono_chain.sum(); + assert_eq!(sum, 0); +} + +#[test] +#[ignore] +fn sum_single_link_chain() { + let chrono_chain = ChronoChainTrait::build(array![5]); + + let sum = chrono_chain.sum(); + assert_eq!(sum, 5); +} + +#[test] +#[ignore] +fn sum_two_link_chain() { + let chrono_chain = ChronoChainTrait::build(array![3, 7]); + + let sum = chrono_chain.sum(); + assert_eq!(sum, 10); +} + +#[test] +#[ignore] +fn sum_large_chain() { + let chrono_chain = ChronoChainTrait::build(array![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + + let sum = chrono_chain.sum(); + assert_eq!(sum, 55); +} + +#[test] +#[ignore] +fn sum_large_values_in_chain() { + let chrono_chain = ChronoChainTrait::build(array![U32_MAX, U32_MAX]); + + let expected_sum: u64 = U32_MAX.into() * 2; + let sum = chrono_chain.sum(); + assert_eq!(sum, expected_sum); +} + +#[test] +#[ignore] +fn sum_zero_values_in_chain() { + let chrono_chain = ChronoChainTrait::build(array![0, 0, 0, 0, 0]); + + let sum = chrono_chain.sum(); + assert_eq!(sum, 0); +} + +#[test] +#[ignore] +fn sum_very_large_chain() { + let chrono_chain = ChronoChainTrait::build(array_in_range((1..1000))); + + let expected_sum = 999 * (999 + 1) / 2; + let sum = chrono_chain.sum(); + assert_eq!(sum, expected_sum); +} + +fn array_in_range(range: core::ops::Range) -> Array { + let mut arr = array![]; + for elem in range { + arr.append(elem); + }; + arr +} diff --git a/exercises/concept/chrono-realms-time-tree/.docs/hints.md b/exercises/concept/chrono-realms-time-tree/.docs/hints.md deleted file mode 100644 index e69de29b..00000000 diff --git a/exercises/concept/chrono-realms-time-tree/.docs/instructions.md b/exercises/concept/chrono-realms-time-tree/.docs/instructions.md deleted file mode 100644 index 9522b7f4..00000000 --- a/exercises/concept/chrono-realms-time-tree/.docs/instructions.md +++ /dev/null @@ -1,46 +0,0 @@ -# Instructions - -In **ChronoRealms**, TimeKeepers often deal with not just trees of timelines, but **ChronoChains**-sequences of linked **TimeNodes**, each representing a specific moment in time. -A **ChronoChain** is a straight path of sequential moments, where each **TimeNode** connects to the next. -These **ChronoChains** are useful when traveling through a series of specific events, as they allow TimeKeepers to follow a single timeline. - -However, to handle these potentially long **ChronoChains**, TimeKeepers use **Smart Pointers (Box)** to safely manage and traverse these lists of moments without causing unnecessary memory duplication or overflow. -Each **TimeNode** holds a reference to the next node, forming a recursive structure. - -Your task as an apprentice is to implement a **ChronoChain** as a recursive list structure using smart pointers. - -In this exercise, you will: - -1. Create a recursive `ChronoChain` enum, representing a list of moments. -2. Use the `Box` smart pointer to store the recursive nodes. -3. Implement a function to create a **ChronoChain** from an array of `u32` values. -4. Implement a function to traverse the **ChronoChain** and sum up the values stored in the list. - -## 1. Define the Recursive `ChronoChain` Enum - -Create a recursive enum `ChronoChain` with two variants: - -- `End`: Represents the end of the list. -- `Link`: Contains a `u32` value and a boxed reference to the next node in the chain. - -## 2. Create a Function to Build a ChronoChain - -Write a function `build_chrono_chain` that takes an array of `u32` values and returns a **ChronoChain**, linking the values sequentially using smart pointers. - -## 3. Implement the Sum Function - -Write a function `sum_chain` to recursively traverse the **ChronoChain** and sum the values of all nodes. - -## Example Usage - -```rust -fn main() { - // Create a ChronoChain from an array of values - let chrono_chain = build_chrono_chain([10, 20, 30]); - - // Sum the values in the ChronoChain - let total_sum = sum_chain(&chrono_chain); - - println!("Total Time Power: {}", total_sum); -} -``` diff --git a/exercises/concept/chrono-realms-time-tree/.docs/introduction.md b/exercises/concept/chrono-realms-time-tree/.docs/introduction.md deleted file mode 100644 index e69de29b..00000000 diff --git a/exercises/concept/chrono-realms-time-tree/.meta/design.md b/exercises/concept/chrono-realms-time-tree/.meta/design.md deleted file mode 100644 index e69de29b..00000000 diff --git a/exercises/concept/chrono-realms-time-tree/.meta/exemplar.cairo b/exercises/concept/chrono-realms-time-tree/.meta/exemplar.cairo deleted file mode 100644 index f47658eb..00000000 --- a/exercises/concept/chrono-realms-time-tree/.meta/exemplar.cairo +++ /dev/null @@ -1,27 +0,0 @@ -// Define the recursive ChronoChain enum -#[derive(Copy, Drop)] -pub enum ChronoChain { - End, - Link: (u32, Box), -} - -// Function to build a ChronoChain from an array of u32 values -pub fn build_chrono_chain(arr: Array) -> Box { - let mut chain = ChronoChain::End; - - // Iterate in reverse to build the chain from the end to the beginning - let mut span = arr.span(); - while let Option::Some(value) = span.pop_back() { - chain = ChronoChain::Link((*value, BoxTrait::new(chain))); - }; - - BoxTrait::new(chain) -} - -// Function to sum the values in the ChronoChain -pub fn sum_chain(chain: @ChronoChain) -> u32 { - match chain { - ChronoChain::End => 0, - ChronoChain::Link((value, next)) => *value + sum_chain(@(*next).unbox()), - } -} diff --git a/exercises/concept/chrono-realms-time-tree/src/lib.cairo b/exercises/concept/chrono-realms-time-tree/src/lib.cairo deleted file mode 100644 index 3a558df7..00000000 --- a/exercises/concept/chrono-realms-time-tree/src/lib.cairo +++ /dev/null @@ -1,16 +0,0 @@ -// Define the recursive ChronoChain enum -#[derive(Copy, Drop)] -pub enum ChronoChain { - End, - Link: (u32, Box), -} - -// Function to build a ChronoChain from an array of u32 values -pub fn build_chrono_chain(arr: Array) -> Box { - panic!("implement 'build_chrono_chain'") -} - -// Function to sum the values in the ChronoChain -pub fn sum_chain(chain: @ChronoChain) -> u32 { - panic!("implement 'sum_chain'") -} diff --git a/exercises/concept/chrono-realms-time-tree/tests/chrono_realms_time_tree.cairo b/exercises/concept/chrono-realms-time-tree/tests/chrono_realms_time_tree.cairo deleted file mode 100644 index f5dbe78a..00000000 --- a/exercises/concept/chrono-realms-time-tree/tests/chrono_realms_time_tree.cairo +++ /dev/null @@ -1,38 +0,0 @@ -use chrono_realms_time_tree::{ChronoChain, build_chrono_chain, sum_chain}; - -#[test] -fn test_build_chrono_chain() { - let chrono_chain = build_chrono_chain(array![10, 20, 30]); - // Verify the structure of the chain - if let ChronoChain::Link((first, next)) = chrono_chain.unbox() { - assert_eq!(first, 10); - - if let ChronoChain::Link((second, next)) = next.unbox() { - assert_eq!(second, 20); - - if let ChronoChain::Link((third, next)) = next.unbox() { - assert_eq!(third, 30); - - if let ChronoChain::Link(_) = next.unbox() { - panic!("Expected 4th elements to be ChronoChain::End"); - } - } - } - } -} - -#[test] -fn test_sum_chain() { - let chrono_chain = build_chrono_chain(array![10, 20, 30]); - let total_sum = sum_chain(@chrono_chain.unbox()); - assert_eq!(total_sum, 60); // 10 + 20 + 30 = 60 -} - -#[test] -fn test_empty_chain() { - // Empty chain should sum to 0 - let empty_chain = BoxTrait::new(ChronoChain::End); - let total_sum = sum_chain(@empty_chain.unbox()); - assert_eq!(total_sum, 0); -} - From 1d45e759b236c74908a189f4f0cb82db045503e7 Mon Sep 17 00:00:00 2001 From: Nenad Date: Mon, 30 Dec 2024 08:09:04 +0100 Subject: [PATCH 5/8] implement operator overloading concept (#329) --- .../operator-overloading/.meta/config.json | 6 +-- concepts/operator-overloading/about.md | 49 +++++++++++++++++++ concepts/operator-overloading/introduction.md | 4 ++ concepts/operator-overloading/links.json | 11 ++++- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/concepts/operator-overloading/.meta/config.json b/concepts/operator-overloading/.meta/config.json index 35b1d32b..d57d8b6b 100644 --- a/concepts/operator-overloading/.meta/config.json +++ b/concepts/operator-overloading/.meta/config.json @@ -1,7 +1,5 @@ { - "blurb": "", - "authors": [ - "" - ], + "blurb": "Operator overloading in Cairo enables you to redefine standard operators like `+` and `-` for custom types by implementing specific traits.", + "authors": ["0xNeshi"], "contributors": [] } diff --git a/concepts/operator-overloading/about.md b/concepts/operator-overloading/about.md index 6917574a..8e21e5b7 100644 --- a/concepts/operator-overloading/about.md +++ b/concepts/operator-overloading/about.md @@ -1 +1,50 @@ # Operator Overloading + +Operator overloading is a feature in some programming languages that allows the redefinition of standard operators, such as addition (`+`), subtraction (`-`), multiplication (`*`), and division (`/`), to work with user-defined types. + +This can make the syntax of the code more intuitive, by enabling operations on user-defined types to be expressed in the same way as operations on primitive types. + +In Cairo, operator overloading is achieved through the implementation of specific traits. + +Each operator has an associated trait, and overloading that operator involves providing an implementation of that trait for a custom type. +However, it's essential to use operator overloading judiciously. + +Misuse can lead to confusion, making the code more difficult to maintain, for example when there is no semantic meaning to the operator being overloaded. + +Consider an example where two `Potions` need to be combined. + +`Potions` have two data fields, mana and health. + +Combining two `Potions` should add their respective fields. + +```rust +struct Potion { + health: felt252, + mana: felt252, +} + +impl PotionAdd of Add { + fn add(lhs: Potion, rhs: Potion) -> Potion { + Potion { health: lhs.health + rhs.health, mana: lhs.mana + rhs.mana } + } +} + +fn main() { + let health_potion: Potion = Potion { health: 100, mana: 0 }; + let mana_potion: Potion = Potion { health: 0, mana: 100 }; + let super_potion: Potion = health_potion + mana_potion; + // Both potions were combined with the `+` operator. + assert(super_potion.health == 100, ''); + assert(super_potion.mana == 100, ''); +} +``` + +In the code above, we're implementing the `Add` trait for the `Potion` type. + +The add function takes two arguments: `lhs` and `rhs` (left and right-hand side). + +The function body returns a new `Potion` instance, its field values being a combination of `lhs` and `rhs`. + +As illustrated in the example, overloading an operator requires specification of the concrete type being overloaded. + +The overloaded generic trait is `Add`, and we define a concrete implementation for the type `Potion` with `Add`. diff --git a/concepts/operator-overloading/introduction.md b/concepts/operator-overloading/introduction.md index e10b99d0..a8a394da 100644 --- a/concepts/operator-overloading/introduction.md +++ b/concepts/operator-overloading/introduction.md @@ -1 +1,5 @@ # Introduction + +Operator overloading in Cairo enables you to redefine standard operators like `+` and `-` for custom types by implementing specific traits. +This allows user-defined types to interact intuitively with operators, similar to primitive types. +When used thoughtfully, operator overloading enhances code readability and expressiveness while preserving clarity and intent. diff --git a/concepts/operator-overloading/links.json b/concepts/operator-overloading/links.json index fe51488c..87fd948d 100644 --- a/concepts/operator-overloading/links.json +++ b/concepts/operator-overloading/links.json @@ -1 +1,10 @@ -[] +[ + { + "url": "https://book.cairo-lang.org/ch12-03-operator-overloading.html", + "description": "Operator Overloading in The Cairo Book" + }, + { + "url": "https://book.cairo-lang.org/appendix-02-operators-and-symbols.html", + "description": "Operators and Symbols in The Cairo Book" + } +] From 7f33be6b9288a1782f42347085b4664820cb5e8b Mon Sep 17 00:00:00 2001 From: Nenad Date: Mon, 30 Dec 2024 08:09:16 +0100 Subject: [PATCH 6/8] Write the "Smart Pointers" concept (#327) * implement smart-pointers * minor fixes to control-flow wording * remove duplicate heading --- concepts/control-flow/.meta/config.json | 2 +- concepts/control-flow/links.json | 2 +- concepts/smart-pointers/.meta/config.json | 6 +- concepts/smart-pointers/about.md | 126 ++++++++++++++++++++++ concepts/smart-pointers/introduction.md | 4 + concepts/smart-pointers/links.json | 11 +- 6 files changed, 144 insertions(+), 7 deletions(-) diff --git a/concepts/control-flow/.meta/config.json b/concepts/control-flow/.meta/config.json index 17756acf..6058fa32 100644 --- a/concepts/control-flow/.meta/config.json +++ b/concepts/control-flow/.meta/config.json @@ -1,5 +1,5 @@ { - "blurb": "Control flow in Cairo uses `if` expressions to conditionally execute code and loops to run code repeatedly.", + "blurb": "Control flow in Cairo uses `if` expressions to conditionally execute code, and loops to run code repeatedly.", "authors": ["Falilah"], "contributors": ["0xNeshi"] } diff --git a/concepts/control-flow/links.json b/concepts/control-flow/links.json index 2cc370a3..efe514a5 100644 --- a/concepts/control-flow/links.json +++ b/concepts/control-flow/links.json @@ -1,6 +1,6 @@ [ { "url": "https://book.cairo-lang.org/ch02-05-control-flow.html", - "description": "Control flow concept in cairo book" + "description": "Control Flow Concept in The Cairo Book" } ] diff --git a/concepts/smart-pointers/.meta/config.json b/concepts/smart-pointers/.meta/config.json index 35b1d32b..f1030f65 100644 --- a/concepts/smart-pointers/.meta/config.json +++ b/concepts/smart-pointers/.meta/config.json @@ -1,7 +1,5 @@ { - "blurb": "", - "authors": [ - "" - ], + "blurb": "Smart pointers in Cairo are special data structures that manage memory safely by ensuring that memory is accessed in a controlled way.", + "authors": ["0xNeshi"], "contributors": [] } diff --git a/concepts/smart-pointers/about.md b/concepts/smart-pointers/about.md index bf8e8d62..435e99c6 100644 --- a/concepts/smart-pointers/about.md +++ b/concepts/smart-pointers/about.md @@ -1 +1,127 @@ # Smart Pointers + +Smart pointers in Cairo are a powerful tool that provide safe and efficient management of memory. + +A smart pointer is a data structure that acts like a regular pointer, but with added safety features to avoid common memory management issues like dereferencing null pointers or accessing uninitialized memory. + +## What is a Smart Pointer? + +In general, a pointer is a variable that stores a memory address, typically pointing to a value stored at that location. + +However, raw pointers can be dangerous: if the pointer doesn't point to valid memory or is incorrectly dereferenced, it can lead to crashes or unpredictable behavior. + +Smart pointers solve this issue by enforcing strict rules on memory management, ensuring that memory is accessed in a safe and controlled manner. + +Cairo, like many modern languages such as Rust, uses smart pointers to prevent unsafe memory access. + +A smart pointer in Cairo not only stores a memory address but also tracks ownership and ensures memory safety. + +## Types of Smart Pointers in Cairo + +Cairo provides several types of smart pointers, including `Box` and `Nullable`, each serving a different purpose. + +Let's take a closer look at how these types work and when you might use them. + +### `Box` + +The `Box` type is the principal smart pointer in Cairo. + +It allows you to store data in a special memory segment called the "boxed segment." A `Box` is a pointer that points to this segment, and when you create a box, you allocate space for the data in this segment. + +Boxes are ideal in situations where: + +- You need to store a value of a type whose size cannot be determined at compile time. +- You have a large amount of data and want to transfer ownership without copying it. + +By using a box, you can store large data structures more efficiently. + +When passing a `Box` to a function, only the pointer is passed, avoiding the need to copy the entire data, which improves performance, especially with large structures. + +### `Nullable` + +The `Nullable` type is another important smart pointer in Cairo. + +It can either point to a valid value of type `T` or be `null` if there is no value. + +This type is useful in cases where you need to store values that may not always exist, such as in a dictionary that can contain optional elements. + +In Cairo, `Nullable` is typically used in dictionaries to handle cases where no default value can be applied. + +It allows you to store values conditionally, making it easier to handle the absence of data safely. + +## Memory Safety with Smart Pointers + +One of the primary advantages of using smart pointers is that they help ensure memory safety. + +By managing ownership and access rules, Cairo prevents common issues such as dereferencing null or dangling pointers. + +Smart pointers track when data is no longer needed and can automatically deallocate memory when it goes out of scope, reducing the risk of memory leaks. + +### Example: Using a `Box` for Recursive Types + +A common challenge in many programming languages is handling recursive types. + +Recursive types refer to types that contain references to themselves. + +Without proper memory management, defining a recursive type can lead to issues such as infinite recursion. + +In Cairo, `Box` makes it possible to define recursive types safely. + +For instance, you can use `Box` to implement a binary tree, where each node contains a reference to another node. + +By storing references in boxes, Cairo ensures that memory usage is finite, and the compiler can determine the required memory size for the structure. + +```rust +use core::box::{BoxTrait}; + +#[derive(Copy, Drop)] +enum BinaryTree { + Leaf: u32, + Node: (u32, Box, Box), +} + +fn main() { + let leaf1 = BinaryTree::Leaf(1); + let leaf2 = BinaryTree::Leaf(2); + let node = BinaryTree::Node((3, BoxTrait::new(leaf1), BoxTrait::new(leaf2))); + println!("{:?}", node); +} +``` + +### Performance Benefits of Smart Pointers + +Smart pointers also improve the performance of your programs. + +When you use `Box`, only the pointer to the data is passed around, instead of copying the entire data structure. + +This is especially useful when dealing with large datasets, as it significantly reduces the overhead of memory operations. + +In the following example, the data is passed by reference using a `Box` to avoid the cost of copying the entire structure: + +```rust +#[derive(Drop)] +struct Cart { + paid: bool, + items: u256, + buyer: ByteArray, +} + +fn pass_data(cart: Cart) { + println!("{} is shopping today and bought {} items", cart.buyer, cart.items); +} + +fn pass_pointer(cart: Box) { + let cart = cart.unbox(); + println!("{} is shopping today and bought {} items", cart.buyer, cart.items); +} + +fn main() { + let new_cart = Cart { paid: true, items: 1, buyer: "John" }; + pass_data(new_cart); + + let new_box = BoxTrait::new(Cart { paid: false, items: 3, buyer: "Jane" }); + pass_pointer(new_box); +} +``` + +In this example, `pass_pointer` takes a `Box` instead of a `Cart`, reducing the amount of memory copied during the function call. diff --git a/concepts/smart-pointers/introduction.md b/concepts/smart-pointers/introduction.md index e10b99d0..e2481dc8 100644 --- a/concepts/smart-pointers/introduction.md +++ b/concepts/smart-pointers/introduction.md @@ -1 +1,5 @@ # Introduction + +Smart pointers in Cairo are special data structures that manage memory safely by ensuring that memory is accessed in a controlled way. +They act like regular pointers but include additional features like ownership rules and metadata to prevent common errors, such as dereferencing uninitialized memory. +This makes them essential for working with complex data structures and improving program safety and performance. diff --git a/concepts/smart-pointers/links.json b/concepts/smart-pointers/links.json index fe51488c..743fa914 100644 --- a/concepts/smart-pointers/links.json +++ b/concepts/smart-pointers/links.json @@ -1 +1,10 @@ -[] +[ + { + "url": "https://book.cairo-lang.org/ch11-02-smart-pointers.html", + "description": "Smart Pointers in The Cairo Book" + }, + { + "url": "https://book.cairo-lang.org/ch03-02-dictionaries.html#dictionaries-of-types-not-supported-natively", + "description": "Nullable Use Case with Dictionaries" + } +] From 7f6e97a8730672db3fa27f7f4d437844e54ad56c Mon Sep 17 00:00:00 2001 From: Nenad Date: Tue, 31 Dec 2024 08:08:18 +0100 Subject: [PATCH 7/8] implement associated items concept (#330) * implement associated items concept * fix combine fn example --- concepts/associated-items/.meta/config.json | 5 + concepts/associated-items/about.md | 127 ++++++++++++++++++++ concepts/associated-items/introduction.md | 5 + concepts/associated-items/links.json | 6 + config.json | 5 + 5 files changed, 148 insertions(+) create mode 100644 concepts/associated-items/.meta/config.json create mode 100644 concepts/associated-items/about.md create mode 100644 concepts/associated-items/introduction.md create mode 100644 concepts/associated-items/links.json diff --git a/concepts/associated-items/.meta/config.json b/concepts/associated-items/.meta/config.json new file mode 100644 index 00000000..0ed435e4 --- /dev/null +++ b/concepts/associated-items/.meta/config.json @@ -0,0 +1,5 @@ +{ + "blurb": "Associated implementations in Cairo allow you to enforce relationships between types and their trait implementations, ensuring type safety and consistency.", + "authors": ["0xNeshi"], + "contributors": [] +} diff --git a/concepts/associated-items/about.md b/concepts/associated-items/about.md new file mode 100644 index 00000000..87ca76d2 --- /dev/null +++ b/concepts/associated-items/about.md @@ -0,0 +1,127 @@ +# Associated Items + +Associated items are items declared in traits or defined in implementations. + +These include: + +- **Associated functions** (like methods) +- **Associated types** +- **Associated constants** +- **Associated implementations** + +They group logically related functionality with a type. + +For example, the `is_some` method in `Option` is intrinsically tied to `Option`. + +## Associated Types + +Associated types define placeholders for types in traits, which are specified by implementors. + +This keeps trait definitions flexible and concise. + +Example: + +```rust +trait Pack { + type Result; + + fn pack(self: T, other: T) -> Self::Result; +} + +impl PackU32Impl of Pack { + type Result = u64; + + fn pack(self: u32, other: u32) -> Self::Result { + let shift: u64 = 0x100000000; // 2^32 + self.into() * shift + other.into() + } +} +``` + +Here, `Result` is an associated type determined by each implementation. + +A function using this trait doesn't need to specify extra generics: + +```rust +fn combine>(a: T, b: T) -> PackImpl::Result { + a.pack(b) +} +``` + +## Associated Constants + +Associated constants are fixed values tied to a type and defined in a trait or its implementation. + +Example: + +```rust +trait Shape { + const SIDES: u32; + fn describe() -> ByteArray; +} + +struct Triangle {} + +impl TriangleShape of Shape { + const SIDES: u32 = 3; + fn describe() -> ByteArray { + "I am a triangle." + } +} + +struct Square {} + +impl SquareShape of Shape { + const SIDES: u32 = 4; + fn describe() -> ByteArray { + "I am a square." + } +} +``` + +This ties constants like `SIDES` to `Shape` instead of hardcoding them elsewhere. + +## Associated Implementations + +Associated implementations enforce relationships between types and traits. + +For example: + +```rust +// Collection type that contains a simple array +#[derive(Drop)] +pub struct ArrayIter { + array: Array, +} + +// T is the collection type +pub trait Iterator { + type Item; + fn next(ref self: T) -> Option; +} + +impl ArrayIterator of Iterator> { + type Item = T; + fn next(ref self: ArrayIter) -> Option { + self.array.pop_front() + } +} + +/// Turns a collection of values into an iterator +pub trait IntoIterator { + /// The iterator type that will be created + type IntoIter; + impl Iterator: Iterator; + + fn into_iter(self: T) -> Self::IntoIter; +} + +impl ArrayIntoIterator of IntoIterator> { + type IntoIter = ArrayIter; + fn into_iter(self: Array) -> Self::IntoIter { + ArrayIter { array: self } + } +} +``` + +This guarantees `Array` can always be converted into an iterator (`IntoIter`), ensuring consistency and type safety. diff --git a/concepts/associated-items/introduction.md b/concepts/associated-items/introduction.md new file mode 100644 index 00000000..c51d5a20 --- /dev/null +++ b/concepts/associated-items/introduction.md @@ -0,0 +1,5 @@ +# Introduction + +Associated implementations in Cairo allow you to enforce relationships between types and their trait implementations, ensuring type safety and consistency. +By specifying that an associated type must implement a particular trait, you create a strong, compile-time guarantee that the necessary functionality is provided. +This feature is especially useful for building modular, extensible, and type-safe abstractions. diff --git a/concepts/associated-items/links.json b/concepts/associated-items/links.json new file mode 100644 index 00000000..a240a43b --- /dev/null +++ b/concepts/associated-items/links.json @@ -0,0 +1,6 @@ +[ + { + "url": "https://book.cairo-lang.org/ch12-10-associated-items.html", + "description": "Associated Items in the Cairo book" + } +] diff --git a/config.json b/config.json index a6183bd3..c421ec96 100644 --- a/config.json +++ b/config.json @@ -955,6 +955,11 @@ "slug": "functions", "name": "Functions" }, + { + "uuid": "56b0dcb7-c9c3-4323-b297-873002b713d3", + "slug": "associated-items", + "name": "Associated Items" + }, { "uuid": "9be30113-b4fe-4cdd-9894-9afd593f2a90", "slug": "generics", From 20ddbc8150f744f140f2e4737f07e067eacd6a52 Mon Sep 17 00:00:00 2001 From: Nenad Date: Tue, 31 Dec 2024 08:08:41 +0100 Subject: [PATCH 8/8] implement method syntax concept (#331) --- concepts/method-syntax/.meta/config.json | 6 +-- concepts/method-syntax/about.md | 60 ++++++++++++++++++++++++ concepts/method-syntax/introduction.md | 4 ++ concepts/method-syntax/links.json | 7 ++- 4 files changed, 72 insertions(+), 5 deletions(-) diff --git a/concepts/method-syntax/.meta/config.json b/concepts/method-syntax/.meta/config.json index 35b1d32b..967eec7f 100644 --- a/concepts/method-syntax/.meta/config.json +++ b/concepts/method-syntax/.meta/config.json @@ -1,7 +1,5 @@ { - "blurb": "", - "authors": [ - "" - ], + "blurb": "In Cairo, methods and associated functions allow you to organize functionality around specific types, making your code modular and intuitive.", + "authors": ["0xNeshi"], "contributors": [] } diff --git a/concepts/method-syntax/about.md b/concepts/method-syntax/about.md index 5dfaae2c..7f81fd03 100644 --- a/concepts/method-syntax/about.md +++ b/concepts/method-syntax/about.md @@ -1 +1,61 @@ # Method Syntax + +Methods in Cairo are similar to functions, but they are tied to a specific type through traits. + +Their first parameter is always `self`, representing the instance on which the method is called. + +While Cairo doesn’t allow defining methods directly on a type, you can achieve the same functionality by defining a trait and implementing it for the type. + +Here’s an example of defining a method on a `Rectangle` type using a trait: + +```rust +#[derive(Copy, Drop)] +struct Rectangle { + width: u64, + height: u64, +} + +#[generate_trait] +impl RectangleImpl of RectangleTrait { + fn area(self: @Rectangle) -> u64 { + (*self.width) * (*self.height) + } +} + +fn main() { + let rect = Rectangle { width: 30, height: 50 }; + println!("Area is {}", rect.area()); +} +``` + +In the example above, the `area` method calculates the area of a rectangle. + +Using the `#[generate_trait]` attribute simplifies the process by automatically creating the required trait for you. + +This makes your code cleaner while still allowing methods to be associated with specific types. + +## Associated Functions + +Associated functions are similar to methods but don’t operate on an instance of a type—they don’t take `self` as a parameter. + +These functions are often used as constructors or utility functions tied to the type. + +```rust +#[generate_trait] +impl RectangleImpl of RectangleTrait { + fn square(size: u64) -> Rectangle { + Rectangle { width: size, height: size } + } +} + +fn main() { + let square = RectangleTrait::square(10); + println!("Square dimensions: {}x{}", square.width, square.height); +} +``` + +Associated functions, like `Rectangle::square`, use the `::` syntax and are namespaced to the type. + +They make it easy to create or work with instances without requiring an existing object. + +By organizing related functionality into traits and implementations, Cairo enables clean, modular, and extensible code structures. diff --git a/concepts/method-syntax/introduction.md b/concepts/method-syntax/introduction.md index e10b99d0..46857542 100644 --- a/concepts/method-syntax/introduction.md +++ b/concepts/method-syntax/introduction.md @@ -1 +1,5 @@ # Introduction + +In Cairo, methods and associated functions allow you to organize functionality around specific types, making your code modular and intuitive. +Methods operate on an instance of a type and always include `self` as their first parameter, while associated functions, such as constructors, don’t require an instance. +Together, they enable a clean and structured approach to defining behavior and utilities for your types. diff --git a/concepts/method-syntax/links.json b/concepts/method-syntax/links.json index fe51488c..8045e5b7 100644 --- a/concepts/method-syntax/links.json +++ b/concepts/method-syntax/links.json @@ -1 +1,6 @@ -[] +[ + { + "url": "https://book.cairo-lang.org/ch05-03-method-syntax.html", + "description": "Method Syntax in The Cairo Book" + } +]