diff --git a/src/ch08-00-generic-types-and-traits.md b/src/ch08-00-generic-types-and-traits.md index f3b2959a2..eeb8d6d4f 100644 --- a/src/ch08-00-generic-types-and-traits.md +++ b/src/ch08-00-generic-types-and-traits.md @@ -2,7 +2,7 @@ Every programming language has tools for effectively handling the duplication of concepts. In Cairo, one such tool is generics: abstract stand-ins for concrete types or other properties. We can express the behavior of generics or how they relate to other generics without knowing what will be in their place when compiling and running the code. -Functions can take parameters of some generic type, instead of a concrete type like `u32` or `bool`, in the same way a function takes parameters with unknown values to run the same code on multiple concrete values. In fact, we’ve already used generics in [Chapter 6](ch06-01-enums.md) with `Option`. +Functions can take parameters of some generic type, instead of a concrete type like `u32` or `bool`, in the same way a function takes parameters with unknown values to run the same code on multiple concrete values. In fact, we’ve already used generics in [Chapter 6][option enum] with `Option`. In this chapter, you’ll explore how to define your own types, functions, and traits with generics. @@ -10,6 +10,8 @@ Generics allow us to replace specific types with a placeholder that represents m Then you’ll learn how to use traits to define behavior in a generic way. You can combine traits with generic types to constrain a generic type to accept only those types that have a particular behavior, as opposed to just any type. +[option enum]: ./ch06-01-enums.html#the-option-enum-and-its-advantages + ## Removing Duplication by Extracting a Function Generics allow us to replace specific types with a placeholder that represents multiple types to remove code duplication. Before diving into generics syntax, then, let’s first look at how to remove duplication in a way that doesn’t involve generic types by extracting a function that replaces specific values with a placeholder that represents multiple values. Then we’ll apply the same technique to extract a generic function! By learning how to identify duplicated code that can be extracted into a function, you'll start to recognize instances where generics can be used to reduce duplication. diff --git a/src/ch08-01-generic-data-types.md b/src/ch08-01-generic-data-types.md index 8be90866b..2013b444e 100644 --- a/src/ch08-01-generic-data-types.md +++ b/src/ch08-01-generic-data-types.md @@ -20,7 +20,7 @@ The `largest_list` function compares two lists of the same type and returns the {{#rustdoc_include ../listings/ch08-generic-types-and-traits/no_listing_02_with_tdrop/src/lib.cairo}} ``` -The new `largest_list` function includes in its definition the requirement that whatever generic type is placed there, it must be droppable. The `main` function remains unchanged, the compiler is smart enough to deduce which concrete type is being used and if it implements the `Drop` trait. +The new `largest_list` function includes in its definition the requirement that whatever generic type is placed there, it must be droppable. This is what we call _trait bounds_. The `main` function remains unchanged, the compiler is smart enough to deduce which concrete type is being used and if it implements the `Drop` trait. ### Constraints for Generic Types @@ -40,7 +40,7 @@ When indexing on `list`, the value results in a snap of the indexed element, unl {{#rustdoc_include ../listings/ch08-generic-types-and-traits/no_listing_04_with_tcopy/src/lib.cairo}} ``` -### Anonymous Generic Implementation Parameter (`+` operator) +### Anonymous Generic Implementation Parameter (`+` Operator) Until now, we have always specified a name for each implementation of the required generic trait: `TPartialOrd` for `PartialOrd`, `TDrop` for `Drop`, and `TCopy` for `Copy`. diff --git a/src/ch08-02-traits-in-cairo.md b/src/ch08-02-traits-in-cairo.md index 1b1d7fe38..8827058a4 100644 --- a/src/ch08-02-traits-in-cairo.md +++ b/src/ch08-02-traits-in-cairo.md @@ -6,7 +6,9 @@ We can use _trait bounds_ to specify that a generic type can be any type that ha > Note: Traits are similar to a feature often called interfaces in other languages, although with some differences. -While traits can be written to not accept generic types, they are most useful when used with generic types. We already covered generics in the [previous chapter](./ch08-01-generic-data-types.md), and we will use them in this chapter to demonstrate how traits can be used to define shared behavior for generic types. +While traits can be written to not accept generic types, they are most useful when used with generic types. We already covered generics in the [previous chapter][generics], and we will use them in this chapter to demonstrate how traits can be used to define shared behavior for generic types. + +[generics]: ./ch08-01-generic-data-types.md ## Defining a Trait @@ -106,10 +108,6 @@ and pass in any instance of `NewsArticle` or `Tweet`. Code that calls the function with any other type, such as a `String` or an `i32`, won’t compile because those types don’t implement `Summary`. --> - - - - ## Managing and Using External Trait @@ -153,6 +151,6 @@ In Listing {{#ref negative-impls}}, we define a `ProducerType` that implements t ``` {{#label negative-impls}} - Listing {{#ref negative-impls}}: Using negative impls to enforce that a type cannot implement both Producer and Consumer traits simultaneously + Listing {{#ref negative-impls}}: Using negative impls to enforce that a type cannot implement both `Producer` and `Consumer` traits simultaneously In the `main` function, we create instances of `ProducerType`, `AnotherType`, and `AThirdType`. We then call the `produce` method on the `producer` instance and pass the result to the `consume` method on the `another_type` and `third_type` instances. Finally, we try to call the `consume` method on the `producer` instance, which results in a compile-time error because `ProducerType` does not implement the `Consumer` trait. diff --git a/src/ch09-01-unrecoverable-errors-with-panic.md b/src/ch09-01-unrecoverable-errors-with-panic.md index b48afaf29..88f49a142 100644 --- a/src/ch09-01-unrecoverable-errors-with-panic.md +++ b/src/ch09-01-unrecoverable-errors-with-panic.md @@ -66,7 +66,9 @@ If you try to compile this function that includes code that may panic, you will {{#include ../listings/ch09-error-handling/no_listing_05_nopanic_wrong/output.txt}} ``` -Note that there are two functions that may panic here, `assert` and equality with `==`. We usually don't use `assert` function in practice and use `assert!` macro instead. We will discuss `assert!` macro in more detail in [Testing Cairo Programs](ch10-01-how-to-write-tests.md#checking-results-with-the-assert-macro) chapter. +Note that there are two functions that may panic here, `assert` and equality with `==`. We usually don't use `assert` function in practice and use `assert!` macro instead. We will discuss `assert!` macro in more detail in [Testing Cairo Programs][assert macro] chapter. + +[assert macro]: ./ch10-01-how-to-write-tests.md#checking-results-with-the-assert-macro ## `panic_with` Attribute diff --git a/src/ch09-02-recoverable-errors.md b/src/ch09-02-recoverable-errors.md index a95bfd3c2..dfbe0eb16 100644 --- a/src/ch09-02-recoverable-errors.md +++ b/src/ch09-02-recoverable-errors.md @@ -4,7 +4,7 @@ Most errors aren’t serious enough to require the program to stop entirely. Som ## The `Result` Enum -Recall from [Generic data types](ch08-01-generic-data-types.md#enums) section in Chapter 8 that the `Result` enum is defined as having two variants, `Ok` and `Err`, as follows: +Recall from [Generic data types][generic enums] section in Chapter 8 that the `Result` enum is defined as having two variants, `Ok` and `Err`, as follows: ```rust,noplayground {{#include ../listings/ch09-error-handling/no_listing_07_result_enum/src/lib.cairo}} @@ -12,6 +12,8 @@ Recall from [Generic data types](ch08-01-generic-data-types.md#enums) section in The `Result` enum has two generic types, `T` and `E`, and two variants: `Ok` which holds the value of type `T` and `Err` which holds the value of type `E`. This definition makes it convenient to use the `Result` enum anywhere we have an operation that might succeed (by returning a value of type `T`) or fail (by returning a value of type `E`). +[generic enums]: ./ch08-01-generic-data-types.md#enums + ## The `ResultTrait` The `ResultTrait` trait provides methods for working with the `Result` enum, such as unwrapping values, checking whether the `Result` is `Ok` or `Err`, and panicking with a custom message. The `ResultTraitImpl` implementation defines the logic of these methods. @@ -33,7 +35,7 @@ Finally, the `is_ok` and `is_err` methods are utility functions provided by the These methods are helpful when you want to check the success or failure of an operation without consuming the `Result` value, allowing you to perform additional operations or make decisions based on the variant without unwrapping it. -You can find the implementation of the `ResultTrait` [here](https://github.com/starkware-libs/cairo/blob/main/corelib/src/result.cairo#L20). +You can find the implementation of the `ResultTrait` [here][result corelib]. It is always easier to understand with examples. Have a look at this function signature: @@ -66,12 +68,15 @@ Our two test cases are: {{#rustdoc_include ../listings/ch09-error-handling/listing_09_01/src/lib.cairo:tests}} ``` -Don't worry about `#[cfg(test)]` attribute for now. We'll explain in more detail its meaning in the next [Testing Cairo Programs](ch10-01-how-to-write-tests.md) chapter. +Don't worry about `#[cfg(test)]` attribute for now. We'll explain in more detail its meaning in the next [Testing Cairo Programs][tests] chapter. `#[test]` attribute means the function is a test function, and `#[should_panic]` attribute means this test will pass if the test execution panics. The first one tests a valid conversion from `felt252` to `u8`, expecting the `unwrap` method not to panic. The second test function attempts to convert a value that is out of the `u8` range, expecting the `unwrap` method to panic with the error message `Invalid integer`. +[result corelib]: https://github.com/starkware-libs/cairo/blob/main/corelib/src/result.cairo#L20 +[tests]: ./ch10-01-how-to-write-tests.md + ### The `?` Operator The last operator we will talk about is the `?` operator. The `?` operator is used for more idiomatic and concise error handling. When you use the `?` operator on a `Result` or `Option` type, it will do the following: diff --git a/src/ch10-01-how-to-write-tests.md b/src/ch10-01-how-to-write-tests.md index df0dd2135..72a3b577c 100644 --- a/src/ch10-01-how-to-write-tests.md +++ b/src/ch10-01-how-to-write-tests.md @@ -36,7 +36,7 @@ In _lib.cairo_, let's remove the existing content and add a `tests` module conta ``` {{#label first-test}} -Listing {{#ref first-test}}: A simple test function. +Listing {{#ref first-test}}: A simple test function Note the `#[test]` annotation: this attribute indicates this is a test function, so the test runner knows to treat this function as a test. We might also have non-test functions to help set up common scenarios or perform common operations, so we always need to indicate which functions are tests. @@ -60,8 +60,6 @@ It’s possible to mark a test as ignored so it doesn’t run in a particular in Let’s start to customize the test to our own needs. First change the name of the `it_works` function to a different name, such as `exploration`, like so: -Filename: src/lib.cairo - ```rust, noplayground {{#include ../listings/ch10-testing-cairo-programs/listing_10_01/src/lib.cairo:exploration}} ``` @@ -86,7 +84,7 @@ Now we’ll add another test, but this time we’ll make a test that fails! Test ``` {{#label second-test}} -Listing {{#ref second-test}}: Adding a second test in _lib.cairo_ that will fail. +Listing {{#ref second-test}}: Adding a second test in _lib.cairo_ that will fail Run `scarb cairo-test` and you will see the following output: @@ -104,7 +102,7 @@ Now that you’ve seen what the test results look like in different scenarios, l The `assert!` macro, provided by Cairo, is useful when you want to ensure that some condition in a test evaluates to `true`. We give the `assert!` macro a first argument that evaluates to a Boolean. If the value is `true`, nothing happens and the test passes. If the value is `false`, the `assert!` macro calls `panic()` to cause the test to fail with a message we defined as the second argument. Using the `assert!` macro helps us check that our code is functioning in the way we intend. -Remember in [Chapter 5](ch05-03-method-syntax.md), we used a `Rectangle` struct and a `can_hold` method, which are repeated here in Listing {{#ref rectangle}}. Let’s put this code in the _src/lib.cairo_ file, then write some tests for it using the `assert!` macro. +Remember in [Chapter 5][method syntax], we used a `Rectangle` struct and a `can_hold` method, which are repeated here in Listing {{#ref rectangle}}. Let’s put this code in the _src/lib.cairo_ file, then write some tests for it using the `assert!` macro. Filename: src/lib.cairo @@ -113,12 +111,10 @@ Remember in [Chapter 5](ch05-03-method-syntax.md), we used a `Rectangle` struct ``` {{#label rectangle}} -Listing {{#ref rectangle}}: Using the `Rectangle` struct and its `can_hold` method from Chapter 5. +Listing {{#ref rectangle}}: Using the `Rectangle` struct and its `can_hold` method from Chapter 5 The `can_hold` method returns a `bool`, which means it’s a perfect use case for the `assert!` macro. We can write a test that exercises the `can_hold` method by creating a `Rectangle` instance that has a width of `8` and a height of `7` and asserting that it can hold another `Rectangle` instance that has a width of `5` and a height of `1`. -Filename: src/lib.cairo - ```rust {{#rustdoc_include ../listings/ch10-testing-cairo-programs/listing_10_03/src/lib.cairo:test1}} ``` @@ -141,6 +137,9 @@ It does pass! Let’s add another test, this time asserting that a smaller recta {{#rustdoc_include ../listings/ch10-testing-cairo-programs/listing_10_03/src/lib.cairo:test2}} ``` +{{#label another-test}} +Listing {{#ref another-test}}: Adding another test in _lib.cairo_ that will pass + Because the correct result of the `can_hold` method, in this case, is `false`, we need to negate that result before we pass it to the `assert!` macro. As a result, our test will pass if `can_hold` returns false: ```shell @@ -169,6 +168,8 @@ Error: test result: FAILED. 1 passed; 1 failed; 0 ignored Our tests caught the bug! Because `larger.width` is `8` and `smaller.width` is `5`, the comparison of the widths in `can_hold` now returns `false` (`8` is not less than `5`) in `larger_can_hold_smaller` test. Notice that `smaller_cannot_hold_larger` test still passes: to make the test fail, the height comparison should also be modified in `can_hold` method, replacing the `>` sign with a `<` sign. +[method syntax]: ./ch05-03-method-syntax.md + ## Testing Equality and Comparisons with the `assert_xx!` Macros ### `assert_eq!` and `assert_ne!` Macros @@ -194,7 +195,7 @@ parameter, then we test this function using `assert_eq!` and `assert_ne!` macros ``` {{#label add_two}} -Listing {{#ref add_two}}: Testing the function `add_two` using `assert_eq!` and `assert_ne!` macros. +Listing {{#ref add_two}}: Testing the function `add_two` using `assert_eq!` and `assert_ne!` macros Let’s check that it passes! @@ -291,7 +292,7 @@ Listing {{#ref assert_macros}} demonstrates how to use these macros: ``` {{#label assert_macros}} -Listing {{#ref assert_macros}}: Example of tests that use the `assert_xx!` macros for comparisons. +Listing {{#ref assert_macros}}: Example of tests that use the `assert_xx!` macros for comparisons In this example, we throw multiple times a `Dice` struct and compare the result. We need to manually implement the `PartialOrd` trait for our struct so that we can compare `Dices` with `lt`, `le`, `gt` and,`ge` functions, which are used by `assert_lt!`, `assert_le!`, `assert_gt!` and `assert_ge!` macros respectively. We also need to derive the `Copy` trait on our `Dice` struct in order to use multiple times the instantiated structs, as comparison functions take ownership of variables. @@ -300,7 +301,7 @@ In this example, we throw multiple times a `Dice` struct and compare the result. You can also add a custom message to be printed with the failure message as optional arguments to `assert!`, `assert_eq!`, and `assert_ne!` macros. Any arguments specified after the required arguments are passed along to the -`format!` macro (discussed in [Printing Chapter](./ch11-09-printing.md#formatting)), so you can pass a format string that contains `{}` placeholders and +`format!` macro (discussed in [Printing Chapter][formatting]), so you can pass a format string that contains `{}` placeholders and values to go in those placeholders. Custom messages are useful for documenting what an assertion means; when a test fails, you’ll have a better idea of what the problem is with the code. @@ -331,6 +332,8 @@ Error: test result: FAILED. 0 passed; 1 failed; 0 ignored We can see the value we actually got in the test output, which would help us debug what happened instead of what we were expecting to happen. +[formatting]: ./ch11-09-printing.md#formatting + ## Checking for panics with `should_panic` In addition to checking return values, it’s important to check that our code handles error conditions as we expect. For example, consider the `Guess` type in Listing {{#ref guess}}: @@ -342,14 +345,12 @@ In addition to checking return values, it’s important to check that our code h ``` {{#label guess}} -Listing {{#ref guess}}: Guess struct and its `new` method. +Listing {{#ref guess}}: Guess struct and its `new` method Other code that uses `Guess` depends on the guarantee that `Guess` instances will contain only values between `1` and `100`. We can write a test that ensures that attempting to create a `Guess` instance with a value outside that range panics. We do this by adding the attribute `should_panic` to our test function. The test passes if the code inside the function panics; the test fails if the code inside the function doesn’t panic. -Filename: src/lib.cairo - ```rust, noplayground {{#include ../listings/ch10-testing-cairo-programs/listing_10_05/src/lib.cairo:test}} ``` @@ -366,8 +367,6 @@ test result: ok. 1 passed; 0 failed; 0 ignored; 0 filtered out; Looks good! Now let’s introduce a bug in our code by removing the condition that the `new` function will panic if the value is greater than `100`: -Filename: src/lib.cairo - ```rust, noplayground {{#rustdoc_include ../listings/ch10-testing-cairo-programs/no_listing_03_wrong_new_impl/src/lib.cairo:here}} ``` @@ -395,14 +394,12 @@ Tests that use `should_panic` can be imprecise. A `should_panic` test would pass ``` {{#label guess-2}} -Listing {{#ref guess-2}}: `new` implementation that panics with different error messages. +Listing {{#ref guess-2}}: `new` implementation that panics with different error messages The test will pass because the value we put in the `should_panic` attribute’s expected parameter is the string that the `Guess::new` method panics with. We need to specify the entire panic message that we expect. To see what happens when a `should_panic` test with an expected message fails, let’s again introduce a bug into our code by swapping the bodies of the `if value < 1` and the `else if value > 100` blocks: -Filename: src/lib.cairo - ```rust, noplayground {{#include ../listings/ch10-testing-cairo-programs/no_listing_04_new_bug/src/lib.cairo:here}} ``` @@ -434,7 +431,7 @@ To demonstrate how to run a single test, we’ll first create two test functions ``` {{#label two-tests}} -Listing {{#ref two-tests}}: Two tests with two different names. +Listing {{#ref two-tests}}: Two tests with two different names We can pass the name of any test function to `cairo-test` to run only that test using the `-f` flag: @@ -454,8 +451,6 @@ We can also specify part of a test name, and any test whose name contains that v Sometimes a few specific tests can be very time-consuming to execute, so you might want to exclude them during most runs of `scarb cairo-test`. Rather than listing as arguments all tests you do want to run, you can instead annotate the time-consuming tests using the `#[ignore]` attribute to exclude them, as shown here: -Filename: src/lib.cairo - ```rust, noplayground {{#include ../listings/ch10-testing-cairo-programs/no_listing_05_ignore_tests/src/lib.cairo}} ``` @@ -479,8 +474,6 @@ When you’re at a point where it makes sense to check the results of the ignore When testing recursive functions or loops, the test is instantiated by default with a maximum amount of gas that it can consume. This prevents running infinite loops or consuming too much gas, and can help you benchmark the efficiency of your implementations. This value is assumed reasonably large enough, but you can override it by adding the `#[available_gas()]` attribute on the test function. The following example shows how to use it: -Filename: src/lib.cairo - ```rust, noplayground {{#include ../listings/ch10-testing-cairo-programs/no_listing_06_test_gas/src/lib.cairo}} ``` diff --git a/src/ch10-02-test-organization.md b/src/ch10-02-test-organization.md index 87695dd0c..193e72bf4 100644 --- a/src/ch10-02-test-organization.md +++ b/src/ch10-02-test-organization.md @@ -16,8 +16,6 @@ The `#[cfg(test)]` annotation on the tests module tells Cairo to compile and run Recall that when we created the new _adder_ project in the first section of this chapter, we wrote this first test: -Filename: src/lib.cairo - ```rust {{#include ../listings/ch10-testing-cairo-programs/no_listing_08_cfg_attr/src/lib.cairo}} ``` diff --git a/src/ch11-01-custom-data-structures.md b/src/ch11-01-custom-data-structures.md index 90b327a76..d4f766560 100644 --- a/src/ch11-01-custom-data-structures.md +++ b/src/ch11-01-custom-data-structures.md @@ -11,7 +11,6 @@ level up. You might try to store the level of the players in an array: ```rust,noplayground {{#include ../listings/ch11-advanced-features/listing_01_array_collection/src/lib.cairo:array_append}} - ``` But then you realize you can't increase the level at a specific index once it's @@ -62,7 +61,7 @@ The implementation, with all restrictions in place, would be as follow: Our database implementation is almost complete, except for one thing: the compiler doesn't know how to make a `UserDatabase` go out of scope, since it doesn't implement the `Drop` trait, nor the `Destruct` trait. Since it has a `Felt252Dict` as a member, it cannot be dropped, so we are forced to implement the `Destruct` trait manually (refer to the [Ownership](ch04-01-what-is-ownership.md#the-drop-trait) chapter for more information). -Using `#[derive(Destruct)]` on top of the `UserDatabase` definition won't work because of the use of [Generic types](./ch08-00-generic-types-and-traits.md) in the struct definition. We need to code the `Destruct` trait implementation by ourselves: +Using `#[derive(Destruct)]` on top of the `UserDatabase` definition won't work because of the use of [Generic types][generics] in the struct definition. We need to code the `Destruct` trait implementation by ourselves: ```rust,noplayground {{#include ../listings/ch11-advanced-features/no_listing_12_dict_struct_member/src/lib.cairo:destruct}} @@ -74,6 +73,8 @@ Implementing `Destruct` for `UserDatabase` was our last step to get a fully f {{#rustdoc_include ../listings/ch11-advanced-features/no_listing_12_dict_struct_member/src/lib.cairo:main}} ``` +[generics]: ./ch08-00-generic-types-and-traits.md + ## Simulating a Dynamic Array with Dicts First, let's think about how we want our mutable dynamic array to behave. What @@ -88,7 +89,7 @@ It should: We can define this interface in Cairo like: -```rust +```rust,noplayground {{#include ../listings/ch11-advanced-features/no_listing_13_cust_struct_vect/src/lib.cairo:trait}} ``` @@ -102,10 +103,9 @@ to values. We'll also store a separate `len` field to track the length. Here is what our struct looks like. We wrap the type `T` inside `Nullable` pointer to allow using any type `T` in our data structure, as explained in the -[Dictionaries](./ch03-02-dictionaries.md#dictionaries-of-types-not-supported-natively) -section: +[Dictionaries][nullable] section: -```rust +```rust,noplayground {{#include ../listings/ch11-advanced-features/no_listing_13_cust_struct_vect/src/lib.cairo:struct}} ``` @@ -126,13 +126,14 @@ The implementation of the rest of the interface is straightforward. The implementation of all the methods defined in our interface can be done as follow : -```rust +```rust,noplayground {{#include ../listings/ch11-advanced-features/no_listing_13_cust_struct_vect/src/lib.cairo:implem}} ``` The full implementation of the `Vec` structure can be found in the -community-maintained library -[Alexandria](https://github.com/keep-starknet-strange/alexandria/tree/main/src/data_structures). +community-maintained library [Alexandria](https://github.com/keep-starknet-strange/alexandria/tree/main/src/data_structures). + +[nullable]: ./ch03-02-dictionaries.md#dictionaries-of-types-not-supported-natively ## Simulating a Stack with Dicts @@ -150,7 +151,7 @@ Let us define what operations we need to create a stack : From these specifications we can define the following interface : -```rust +```rust,noplayground {{#include ../listings/ch11-advanced-features/no_listing_14_cust_struct_stack/src/lib.cairo:trait}} ``` @@ -162,13 +163,13 @@ length of the stack to iterate over it. The Stack struct is defined as: -```rust +```rust,noplayground {{#include ../listings/ch11-advanced-features/no_listing_14_cust_struct_stack/src/lib.cairo:struct}} ``` Next, let's see how our main functions `push` and `pop` are implemented. -```rust +```rust,noplayground {{#include ../listings/ch11-advanced-features/no_listing_14_cust_struct_stack/src/lib.cairo:implem}} ``` @@ -182,8 +183,9 @@ accordingly. The full implementation of the Stack, along with more data structures that you can use in your code, can be found in the community-maintained -[Alexandria](https://github.com/keep-starknet-strange/alexandria/tree/main/src/data_structures) -library, in the "data_structures" crate. +[Alexandria][alexandria data structures] library, in the "data_structures" crate. + +[alexandria data structures]: https://github.com/keep-starknet-strange/alexandria/tree/main/src/data_structures ## Summary diff --git a/src/ch11-03-smart-pointers.md b/src/ch11-03-smart-pointers.md index f5b25c5d9..2eb8b481a 100644 --- a/src/ch11-03-smart-pointers.md +++ b/src/ch11-03-smart-pointers.md @@ -17,9 +17,11 @@ Boxes have very little performance overhead, other than writing their inner valu - When you have a type whose size can’t be known at compile time and you want to use a value of that type in a context that requires an exact size - When you have a large amount of data and you want to transfer ownership but ensure the data won’t be copied when you do so -We’ll demonstrate the first situation in the [“Enabling Recursive Types with Boxes”](./ch11-03-smart-pointers.md#enabling-recursive-types-with-nullable-boxes) section. +We’ll demonstrate the first situation in the [“Enabling Recursive Types with Boxes”][nullable recursive types] section. In the second case, transferring ownership of a large amount of data can take a long time because the data is copied around in memory. To improve performance in this situation, we can store the large amount of data in the boxed segment using a box type. Then, only the small amount of pointer data is copied around in memory, while the data it references stays in one place on the boxed segment. +[nullable recursive types]: ./ch11-03-smart-pointers.md#enabling-recursive-types-with-nullable-boxes + ### Using a `Box` to Store Data in the Boxed Segment Before we discuss the boxed segment storage use cases for `Box`, we’ll cover the syntax and how to interact with values stored within a `Box`. @@ -74,4 +76,6 @@ The illustration above demonstrates how the memory behaves in both cases. The fi If we try to access an element that does not exist in a dictionary, the code will fail if the `zero_default` method cannot be called. -[Chapter 3.2](./ch03-02-dictionaries.md#dictionaries-of-types-not-supported-natively) about dictionaries thoroughly explains how to store a `Span` variable inside a dictionary using the `Nullable` type. Please refer to it for further information. +[Chapter 3.2][dictionary nullable span] about dictionaries thoroughly explains how to store a `Span` variable inside a dictionary using the `Nullable` type. Please refer to it for further information. + +[dictionary nullable span]: /ch03-02-dictionaries.md#dictionaries-of-types-not-supported-natively \ No newline at end of file diff --git a/src/ch11-05-hash.md b/src/ch11-05-hash.md index acedde61f..05cc5496f 100644 --- a/src/ch11-05-hash.md +++ b/src/ch11-05-hash.md @@ -1,16 +1,21 @@ # Hashes -At its essence, hashing is a process of converting input data (often called a message) of any length into a fixed-size value, typically referred to as a "hash." This transformation is deterministic, meaning that the same input will always produce the same hash value. Hash functions are a fundamental component in various fields, including data storage, cryptography, and data integrity verification - and are very often used when developing smart contracts, especially when working with [Merkle trees](https://en.wikipedia.org/wiki/Merkle_tree#Uses). +At its essence, hashing is a process of converting input data (often called a message) of any length into a fixed-size value, typically referred to as a "hash." This transformation is deterministic, meaning that the same input will always produce the same hash value. Hash functions are a fundamental component in various fields, including data storage, cryptography, and data integrity verification - and are very often used when developing smart contracts, especially when working with [Merkle trees][merkle tree wiki]. In this chapter, we will present the two hash functions implemented natively in the Cairo library: `Poseidon` and `Pedersen`. We will discuss when and how to use them, and see examples with Cairo programs. +[merkle tree wiki]: https://en.wikipedia.org/wiki/Merkle_tree#Uses + ### Hash Functions in Cairo The Cairo core library provides two hash functions: Pedersen and Poseidon. -Pedersen hash functions are cryptographic algorithms that rely on [elliptic curve cryptography](https://en.wikipedia.org/wiki/Elliptic-curve_cryptography). These functions perform operations on points along an elliptic curve — essentially, doing math with the locations of these points — which are easy to do in one direction and hard to undo. This one-way difficulty is based on the Elliptic Curve Discrete Logarithm Problem (ECDLP), which is a problem so hard to solve that it ensures the security of the hash function. The difficulty of reversing these operations is what makes the Pedersen hash function secure and reliable for cryptographic purposes. +Pedersen hash functions are cryptographic algorithms that rely on [elliptic curve cryptography][ec wiki]. These functions perform operations on points along an elliptic curve — essentially, doing math with the locations of these points — which are easy to do in one direction and hard to undo. This one-way difficulty is based on the Elliptic Curve Discrete Logarithm Problem (ECDLP), which is a problem so hard to solve that it ensures the security of the hash function. The difficulty of reversing these operations is what makes the Pedersen hash function secure and reliable for cryptographic purposes. + +Poseidon is a family of hash functions designed to be very efficient as algebraic circuits. Its design is particularly efficient for Zero-Knowledge proof systems, including STARKs (so, Cairo). Poseidon uses a method called a 'sponge construction,' which soaks up data and transforms it securely using a process known as the Hades permutation. Cairo's version of Poseidon is based on a three-element state permutation with [specific parameters][poseidon parameters] -Poseidon is a family of hash functions designed to be very efficient as algebraic circuits. Its design is particularly efficient for Zero-Knowledge proof systems, including STARKs (so, Cairo). Poseidon uses a method called a 'sponge construction,' which soaks up data and transforms it securely using a process known as the Hades permutation. Cairo's version of Poseidon is based on a three-element state permutation with [specific parameters](https://github.com/starkware-industries/poseidon/blob/main/poseidon3.txt). +[ec wiki]: https://en.wikipedia.org/wiki/Elliptic-curve_cryptography +[poseidon parameters]: https://github.com/starkware-industries/poseidon/blob/main/poseidon3.txt #### When to Use Them? @@ -22,7 +27,7 @@ The core library makes it easy to work with hashes. The `Hash` trait is implemen The `Hash` trait is accompanied by the `HashStateTrait` and `HashStateExTrait` that define the basic methods to work with hashes. They allow you to initialize a hash state that will contain the temporary values of the hash after each application of the hash function; update the hash state, and finalize it when the computation is completed. `HashStateTrait` and `HashStateExTrait` are defined as follows: -```rust +```rust,noplayground {{#include ../listings/ch11-advanced-features/no_listing_03_hash_trait/src/lib.cairo:hashtrait}} ``` @@ -44,7 +49,7 @@ As our struct derives the `Hash` trait, we can call the function as follows for Pedersen is different from Poseidon, as it starts with a base state. This base state must be of `felt252` type, which forces us to either hash the struct with an arbitrary base state using the `update_with` method, or serialize the struct into an array to loop through all of its fields and hash its elements together. -Here is a short example of Pedersen hashing : +Here is a short example of Pedersen hashing: ```rust {{#rustdoc_include ../listings/ch11-advanced-features/no_listing_04_hash_pedersen/src/lib.cairo:main}} @@ -55,9 +60,9 @@ Here is a short example of Pedersen hashing : Let us look at an example of hashing a struct that contains a `Span`. To hash a `Span` or a struct that contains a `Span` you can use the built-in function `poseidon_hash_span(mut span: Span) -> felt252`. Similarly, you can hash `Array` by calling `poseidon_hash_span` on its span. -First, let us import the following traits and function : +First, let us import the following traits and function: -```rust +```rust,noplayground {{#include ../listings/ch11-advanced-features/no_listing_05_advanced_hash/src/lib.cairo:import}} ``` @@ -65,7 +70,6 @@ Now we define the struct. As you might have noticed, we didn't derive the `Hash` ```rust, noplayground {{#include ../listings/ch11-advanced-features/no_listing_05_advanced_hash/src/lib.cairo:structure}} - ``` In this example, we initialized a `HashState` (`hash`) and updated it and then called the function `finalize()` on the @@ -73,5 +77,4 @@ In this example, we initialized a `HashState` (`hash`) and updated it and then c ```rust {{#rustdoc_include ../listings/ch11-advanced-features/no_listing_05_advanced_hash/src/lib.cairo:main}} - ``` diff --git a/src/ch11-06-macros.md b/src/ch11-06-macros.md index 01aa5d6e0..a596e1cb1 100644 --- a/src/ch11-06-macros.md +++ b/src/ch11-06-macros.md @@ -8,7 +8,7 @@ In some situations, a developer might need to declare a constant that is the res Here is an example of `consteval_int!`: -```rust +```rust,noplayground const a: felt252 = consteval_int!(2 * 2 * 2); ``` diff --git a/src/ch11-07-inlining-in-cairo.md b/src/ch11-07-inlining-in-cairo.md index 68ca06b7d..9f448e64e 100644 --- a/src/ch11-07-inlining-in-cairo.md +++ b/src/ch11-07-inlining-in-cairo.md @@ -32,11 +32,11 @@ Let's introduce a short example to illustrate the mechanisms of inlining in Cair ``` {{#label inlining}} -Listing {{#ref inlining}}: A small Cairo program that adds the return value of 2 functions, with one of them being inlined. +Listing {{#ref inlining}}: A small Cairo program that adds the return value of 2 functions, with one of them being inlined Let's take a look at the corresponding Sierra code to see how inlining works under the hood: -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch11-advanced-features/listing_03_inlining_example/src/inlining.sierra}} ``` @@ -155,7 +155,7 @@ Let's study another program that shows other benefits that inlining may sometime Here is the corresponding Sierra code: -```rust +```rust,noplayground {{#rustdoc_include ../listings/ch11-advanced-features/listing_02_inlining/src/inlining.sierra}} ``` diff --git a/src/ch11-09-printing.md b/src/ch11-09-printing.md index 99d1c2e39..69e9f1a2a 100644 --- a/src/ch11-09-printing.md +++ b/src/ch11-09-printing.md @@ -9,7 +9,7 @@ Cairo provides two macros to print standard data types: - `println!` which prints on a new line, - `print!` with inline printing. -Both take a `ByteArray` string as first parameter (See [Data Types](ch02-02-data-types.md#byte-array-strings)) which can be a simple string to print a message or a string with placeholders to format the way values are printed. +Both take a `ByteArray` string as first parameter (See [Data Types][byte array]) which can be a simple string to print a message or a string with placeholders to format the way values are printed. There are two ways to use these placeholders and both can be mixed: @@ -22,7 +22,11 @@ Here are some examples: {{#include ../listings/ch11-advanced-features/no_listing_08_print_macro/src/lib.cairo}} ``` -> `print!` and `println!` macros use the `Display` trait under the hood, and are therefore used to print the value of types that implement it. This is the case for basic data types, but not for more complexe ones. If you try to print complex data types values with these macros, e.g., for debugging purpose, you will get an error. In that case, you can either [manually implement](./ch11-09-printing.md#printing-custom-data-types) the `Display` trait for your type, or use the `Debug` trait (see [below](./ch11-09-printing.md#print-debug-traces)). +> `print!` and `println!` macros use the `Display` trait under the hood, and are therefore used to print the value of types that implement it. This is the case for basic data types, but not for more complexe ones. If you try to print complex data types values with these macros, e.g., for debugging purpose, you will get an error. In that case, you can either [manually implement][print with display] the `Display` trait for your type, or use the `Debug` trait (see [below][print with debug]). + +[byte array]: ./ch02-02-data-types.md#byte-array-strings +[print with display]: ./ch11-09-printing.md#printing-custom-data-types +[print with debug]: ./ch11-09-printing.md#print-debug-traces ## Formatting @@ -90,4 +94,6 @@ Cairo provides the derivable trait `Debug` to print the value of variables when Note that `assert_xx!` macros used in tests require the provided values to implement the `Debug` trait, as they also print the result in case of assertion failure. -Please refer to the [Derivable Traits](appendix-03-derivable-traits.md#debug-trait-for-printing-and-debugging) appendix for more detail about the `Debug` trait and its usage for printing value when debugging. \ No newline at end of file +Please refer to the [Derivable Traits][debug trait] appendix for more detail about the `Debug` trait and its usage for printing value when debugging. + +[debug trait]: ./appendix-03-derivable-traits.md#debug-trait-for-printing-and-debugging \ No newline at end of file