Skip to content

Commit

Permalink
feat: traits exercises
Browse files Browse the repository at this point in the history
  • Loading branch information
enitrat committed Feb 22, 2023
1 parent 734345d commit c669747
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 1 deletion.
11 changes: 11 additions & 0 deletions exercises/traits/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Traits

A trait is a collection of methods.

Data types can implement traits. To do so, the methods making up the trait are defined for the data type. For example, the `u256` data type implements the `Into` trait. This allows a user to write `1.into()` to convert a felt into a u256.

In this way, traits are somewhat similar to Java interfaces and C++ abstract classes.

Because traits indicate shared behavior between data types, they are useful when writing generics.

- [Traits & Impls](https://link.medium.com/IQGqboBerxb#83b5)
33 changes: 33 additions & 0 deletions exercises/traits/traits1.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// traits1.cairo
// Time to implement some traits!

// Your task is to implement the trait
// `AnimalTrait` for the type `Animal`
//
// Execute `starklings hint traits1` or use the `hint` watch subcommand for a hint.

// Fill in the impl block to make the code work.

#[derive(Copy, Drop)]
struct Animal {
noise: felt
}

trait AnimalTrait<T> {
fn new(noise: felt) -> T;
fn make_noise(self: T) -> felt;
}

impl AnimalImpl of AnimalTrait::<Animal> {
// TODO: implement the trait AnimalTrait for Animal
}

#[test]
fn test_traits1() {
// TODO make the test pass by creating two instances of Animal
// and calling make_noise on them
let cat: Animal = AnimalTrait::new('meow');
assert(cat.make_noise() == 'meow', 'Wrong noise');

assert(cow.make_noise() == 'moo', 'Wrong noise');
}
44 changes: 44 additions & 0 deletions exercises/traits/traits2.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// traits2.cairo

// The previous exercise did not make the distinction
// between different types of animals, but this one does.
// The trait `AnimalTrait` has two functions:
// `new` and `make_noise`.
// `new` should return a new instance of the type
// implementing the trait.
// `make_noise` should return the noise the animal makes.
// The types `Cat` and `Cow` are already defined for you.
// You need to implement the trait `AnimalTrait` for them.

// No hints for this one!
// Execute `starklings hint traits2` or use the `hint` watch subcommand for a hint.

#[derive(Copy, Drop)]
struct Cat {
noise: felt,
}

#[derive(Copy, Drop)]
struct Cow {
noise: felt,
}

trait AnimalTrait<T> {
fn new() -> T;
fn make_noise(self: T) -> felt;
}

impl CatImpl of AnimalTrait::<Cat> {
// TODO: implement the trait Animal for the type Cat
}

// TODO: implement the trait Animal for the type Cow

#[test]
fn test_traits2() {
let kitty: Cat = AnimalTrait::new();
assert(kitty.make_noise() == 'meow', 'Wrong noise');

let cow: Cow = AnimalTrait::new();
assert(cow.make_noise() == 'moo', 'Wrong noise');
}
76 changes: 76 additions & 0 deletions exercises/traits/traits3.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// traits3.cairo
//
// The previous exercise showed how to implement a trait for multiple types.
// This exercise shows how you can implement multiple traits for a single type.
// This is useful when you have types that share some common functionality, but
// also have some unique functionality.

// Execute `starklings hint traits3` or use the `hint` watch subcommand for a hint.

#[derive(Copy, Drop)]
struct Fish {
noise: felt,
distance: u32,
}

#[derive(Copy, Drop)]
struct Dog {
noise: felt,
distance: u32,
}

trait AnimalTrait<T> {
fn new() -> T;
fn make_noise(self: T) -> felt;
fn get_distance(self: T) -> u32;
}

trait FishTrait {
fn swim(ref self: Fish) -> ();
}

trait DogTrait {
fn walk(ref self: Dog) -> ();
}

impl AnimalFishImpl of AnimalTrait::<Fish> {
fn new() -> Fish {
Fish { noise: 'blub', distance: 0_u32 }
}
fn make_noise(self: Fish) -> felt {
self.noise
}
fn get_distance(self: Fish) -> u32 {
self.distance
}
}

impl AnimalDogImpl of AnimalTrait::<Dog> {
fn new() -> Dog {
Dog { noise: 'woof', distance: 0_u32 }
}
fn make_noise(self: Dog) -> felt {
self.noise
}
fn get_distance(self: Dog) -> u32 {
self.distance
}
}

// TODO: implement FishTrait for the type Fish

// TODO: implement DogTrait for the type Dog

#[test]
fn test_traits3() {
// Don't modify this test!
let mut salmon: Fish = AnimalTrait::new();
salmon.swim();
assert(salmon.make_noise() == 'blub', 'Wrong noise');
assert(salmon.get_distance() == 1_u32, 'Wrong distance');

let mut dog: Dog = AnimalTrait::new();
dog.walk();
assert(dog.make_noise() == 'woof', 'Wrong noise');
assert(dog.get_distance() == 1_u32, 'Wrong distance');
}
38 changes: 37 additions & 1 deletion info.toml
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,40 @@ For get_fees: This method takes an additional argument, is there a field in the
Looking at the test functions will also help you understand more about the syntax.
This section will help you understanding more about impls and traits: https://link.medium.com/c8TqX7R3qxb#83b5.
"""
"""


# TRAITS

[[exercises]]
name = "traits1"
path = "exercises/traits/traits1.cairo"
mode = "test"
hint = """
If you want to implement a trait for a type, you have to implement all the methods in the trait.
Based on the signature of the method, you can easily implement it.
In the test, you need to instantiate two objects of type `Animal`.
You can call the method of a trait by using the MyTrait::foo() syntax.
How would you instantiate the two objects with AnimalTrait?
Maybe you need to specify the type of the object?
"""

[[exercises]]
name = "traits2"
path = "exercises/traits/traits2.cairo"
mode = "test"
hint = """ No hints for this one! It is very similar to the previous exercise."""

[[exercises]]
name = "traits3"
path = "exercises/traits/traits3.cairo"
mode = "test"
hint = """
You can implement multiple traits for a type.
When a trait is destined to be implemented by a single type, you don't need to use generics.
If you're having trouble updating the distance value in the `Fish` and `Dog` impls, remember that you need to first
1. Destructure the object into mutable variables
2. Update the distance variable
3. Reconstruct `self` with the updated variables (`self = MyStruct { ... }`)
"""

0 comments on commit c669747

Please sign in to comment.