Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: traits exercises #37

Merged
merged 3 commits into from
Feb 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.

// I AM NOT DONE

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

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

impl AnimalImpl of AnimalTrait {// 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

assert(cat.make_noise() == 'meow', 'Wrong noise');
assert(cow.make_noise() == 'moo', 'Wrong noise');
}
46 changes: 46 additions & 0 deletions exercises/traits/traits2.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 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.

// I AM NOT DONE

#[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');
}
78 changes: 78 additions & 0 deletions exercises/traits/traits3.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// 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.

// I AM NOT DONE

#[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');
}
34 changes: 34 additions & 0 deletions info.toml
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,40 @@ Looking at the test functions will also help you understand more about the synta
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 { ... }`)
"""
[[exercises]]
name = "operations1"
path = "exercises/operations/operations1.cairo"
Expand Down