-
-
Notifications
You must be signed in to change notification settings - Fork 52
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
Add errors concept #686
Merged
Merged
Add errors concept #686
Changes from 7 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
0e60f2e
start
meatball133 cc7aa7b
Add exercise and concept
meatball133 917f2dc
More work
meatball133 c357ffc
Update concept text
meatball133 00bfda8
Update config
meatball133 9a3db50
Format spec file
meatball133 b56d0ba
Update based on feedback
meatball133 4a85a15
Merge branch 'main' into add-errors-concept
meatball133 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"blurb": "Crystal has errors which can be raised and rescued. It is also possible to deffine your own exceptions.", | ||
"authors": ["meatball133"], | ||
"contributors": ["ryanplusplus"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{% $errors %} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
# Exception | ||
|
||
In the ideal world, everything works perfectly. | ||
But in the real world, things can go wrong, and how we handle these situations matters to ensure that our software is robust and reliable. | ||
Exceptions are a crucial concept in programming that allows us to handle errors and unexpected situations gracefully. | ||
|
||
Raising an error, if not handled, halts the program and throws an error message. | ||
In most cases, you don't want your program to halt when an error occurs, instead you want to handle the error and continue running the program. | ||
|
||
## Raising an exception | ||
|
||
In Crystal, exceptions are raised using the `raise` keyword and can either be given a `String` or an `Exception` object. | ||
If unhandled, the program will halt and print the error message. | ||
|
||
```crystal | ||
raise "This is an error" | ||
``` | ||
|
||
There are several built-in exceptions in Crystal, like `ArgumentError`, `IndexError`, `KeyError`, `IOError`, `SystemCallError`, `TypeError`, `ZeroDivisionError` and many more. | ||
These require you to pass a message to the exception. | ||
|
||
```crystal | ||
raise ArgumentError.new("This is an argument error") | ||
``` | ||
|
||
## [Handling exceptions][[exception-handling]] | ||
|
||
We wouldn't want our program to crash when an exception is raised. | ||
Therefore, when we know a piece of code is error prone, we can wrap it in a `begin` block and rescue the exception with a `rescue` block. | ||
The `begin` block marks the beginning of the code that might raise an exception, and the `rescue` block handles the exception. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue | ||
puts "An error occurred!" | ||
end | ||
``` | ||
|
||
The `rescue` block can also be specified with a variable to get the exception object. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue ex | ||
puts "An error occurred: #{ex.message}" | ||
end | ||
``` | ||
|
||
The `rescue` block can also be specified with a specific exception type only to catch that exception. | ||
|
||
```crystal | ||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ArgumentError | ||
puts "An argument error occurred!" | ||
end | ||
|
||
# or | ||
|
||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ex : ArgumentError | ||
puts "An argument error occurred: #{ex.message}" | ||
end | ||
``` | ||
|
||
Multiple `rescue` blocks can be used to handle different types of exceptions. | ||
In the example below, the first `rescue` block will catch an `ArgumentError`, and the second `rescue` block will catch any other exception. | ||
|
||
```crystal | ||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ArgumentError | ||
puts "An argument error occurred!" | ||
rescue | ||
puts "An error occurred!" | ||
end | ||
``` | ||
|
||
The `begin` block can also have an `else` block, which is executed if no exception is raised. | ||
|
||
```crystal | ||
begin | ||
puts "No error occurred" | ||
rescue | ||
puts "An error occurred!" | ||
else | ||
puts "No error occurred" | ||
end | ||
``` | ||
|
||
Lastly, there is an `ensure` block that is always executed, regardless of whether an exception was raised. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue | ||
puts "An error occurred!" | ||
ensure | ||
puts "This is always executed" | ||
end | ||
``` | ||
|
||
## Method convention | ||
|
||
Some methods have two versions, one with `!` and the other without. | ||
This can mean two different things. | ||
One is that the method mutates the object, and the other is that the method can raise an exception. | ||
|
||
But there is also another convention around ending a method with `?` mentioned in the boolean concept. | ||
Some methods raise an exception by default but also have a version ending with `?` which returns `nil` instead of raising an exception. | ||
|
||
This is ideal when you want to avoid an error being raised. | ||
This can benefit performance since it doesn't have to create a stack trace and, if set up correctly, could make the code safer. | ||
|
||
## Custom exceptions | ||
|
||
You can also create your own exceptions by inheriting from the [`Exception`][exception] class. | ||
In doing so, you can optionally override the `initialize` method to set the exception message. | ||
This can be done by assigning an instance variable named `@message` with the message. | ||
|
||
```crystal | ||
class MyException < Exception | ||
def initialize | ||
@message = "This is my exception" | ||
end | ||
end | ||
|
||
raise MyException.new | ||
``` | ||
|
||
[exception-handling]: https://crystal-lang.org/reference/syntax_and_semantics/exception_handling.html | ||
[exception]: https://crystal-lang.org/api/Exception.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
# Exception | ||
|
||
In the ideal world, everything works perfectly. | ||
But in the real world, things can go wrong, and how we handle these situations matters to ensure that our software is robust and reliable. | ||
Exceptions are a crucial concept in programming that allows us to handle errors and unexpected situations gracefully. | ||
|
||
Raising an error, if not handled, halts the program and throws an error message. | ||
In most cases, you don't want your program to halt when an error occurs, instead you want to handle the error and continue running the program. | ||
|
||
## Raising an exception | ||
|
||
In Crystal, exceptions are raised using the `raise` keyword and can either be given a `String` or an `Exception` object. | ||
If unhandled, the program will halt and print the error message. | ||
|
||
```crystal | ||
raise "This is an error" | ||
``` | ||
|
||
There are several built-in exceptions in Crystal, like `ArgumentError`, `IndexError`, `KeyError`, `IOError`, `SystemCallError`, `TypeError`, `ZeroDivisionError` and many more. | ||
These require you to pass a message to the exception. | ||
|
||
```crystal | ||
raise ArgumentError.new("This is an argument error") | ||
``` | ||
|
||
## [Handling exceptions][[exception-handling]] | ||
|
||
We wouldn't want our program to crash when an exception is raised. | ||
Therefore, when we know a piece of code is error prone, we can wrap it in a `begin` block and rescue the exception with a `rescue` block. | ||
The `begin` block marks the beginning of the code that might raise an exception, and the `rescue` block handles the exception. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue | ||
puts "An error occurred!" | ||
end | ||
``` | ||
|
||
The `rescue` block can also be specified with a variable to get the exception object. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue ex | ||
puts "An error occurred: #{ex.message}" | ||
end | ||
``` | ||
|
||
The `rescue` block can also be specified with a specific exception type only to catch that exception. | ||
|
||
```crystal | ||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ArgumentError | ||
puts "An argument error occurred!" | ||
end | ||
|
||
# or | ||
|
||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ex : ArgumentError | ||
puts "An argument error occurred: #{ex.message}" | ||
end | ||
``` | ||
|
||
Multiple `rescue` blocks can be used to handle different types of exceptions. | ||
In the example below, the first `rescue` block will catch an `ArgumentError`, and the second `rescue` block will catch any other exception. | ||
|
||
```crystal | ||
begin | ||
raise ArgumentError.new("This is an argument error") | ||
rescue ArgumentError | ||
puts "An argument error occurred!" | ||
rescue | ||
puts "An error occurred!" | ||
end | ||
``` | ||
|
||
The `begin` block can also have an `else` block, which is executed if no exception is raised. | ||
|
||
```crystal | ||
begin | ||
puts "No error occurred" | ||
rescue | ||
puts "An error occurred!" | ||
else | ||
puts "No error occurred" | ||
end | ||
``` | ||
|
||
Lastly, there is an `ensure` block that is always executed, regardless of whether an exception was raised. | ||
|
||
```crystal | ||
begin | ||
raise "This is an error" | ||
rescue | ||
puts "An error occurred!" | ||
ensure | ||
puts "This is always executed" | ||
end | ||
``` | ||
|
||
## Method convention | ||
|
||
Some methods have two versions, one with `!` and the other without. | ||
This can mean two different things. | ||
One is that the method mutates the object, and the other is that the method can raise an exception. | ||
|
||
But there is also another convention around ending a method with `?` mentioned in the boolean concept. | ||
Some methods raise an exception by default but also have a version ending with `?` which returns `nil` instead of raising an exception. | ||
|
||
This is ideal when you want to avoid an error being raised. | ||
This can benefit performance since it doesn't have to create a stack trace and, if set up correctly, could make the code safer. | ||
|
||
## Custom exceptions | ||
|
||
You can also create your own exceptions by inheriting from the [`Exception`][exception] class. | ||
In doing so, you can optionally override the `initialize` method to set the exception message. | ||
This can be done by assigning an instance variable named `@message` with the message. | ||
|
||
```crystal | ||
class MyException < Exception | ||
def initialize | ||
@message = "This is my exception" | ||
end | ||
end | ||
|
||
raise MyException.new | ||
``` | ||
|
||
[exception-handling]: https://crystal-lang.org/reference/syntax_and_semantics/exception_handling.html | ||
[exception]: https://crystal-lang.org/api/Exception.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[ | ||
{ | ||
"url": "https://crystal-lang.org/reference/syntax_and_semantics/exception_handling.html", | ||
"description": "Crystal docs: exception-handling" | ||
}, | ||
{ | ||
"url": "https://crystal-lang.org/api/Exception.html", | ||
"description": "Crystal API: exception" | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,35 @@ | ||||||
# Hints | ||||||
|
||||||
## 1. Divide the food evenly | ||||||
|
||||||
- Start by writing the function signature of `DivideFood`. | ||||||
It should accept 2 parameters of type `FodderCalculator` and `int` and return two values of types `float64` and `error`. | ||||||
Revisit the [functions concept][concept-functions] if you need more information on how to define functions. | ||||||
- In the function body, call the `FodderAmount` [method][concept-methods] on `FodderCalculator` to fetch the default total amount of fodder for the cows. | ||||||
It will return the actual result and an error. | ||||||
Handle the error via an if-statement as it was explained in the introduction. | ||||||
- After that, call the `FatteningFactor` method and handle the error return value as before. | ||||||
- Now that you have the fodder amount and the factor, you can calculate the final result. | ||||||
You need to divide the fodder by the number of cows (revisit [numbers] for hints on type conversion) and multiply with the factor. Check the introduction for what to return as the error value in case of success. | ||||||
|
||||||
## 2. Check the number of cows | ||||||
|
||||||
- `ValidateInputAndDivideFood` has the same function signature as `DivideFood`. | ||||||
- Since you want to return early in case of an error in Go, you first check whether the number of cows is less or equal than 0 with an if-statement. | ||||||
- If it is, you return an error that you created with `errors.New`. | ||||||
Make sure the message matches the instructions. | ||||||
- If the number of cows is valid, you can proceed to call the existing `DivideFood` function from task 1. | ||||||
|
||||||
## 3. Improve the error handling | ||||||
|
||||||
- Start by creating the `InvalidCowsError` [struct][concept-structs] with two unexported fields that hold the number of cows and the message. | ||||||
- Next, define the `Error` method on that struct (with a pointer receiver). Revisit the exercise introduction for help on how to do this. | ||||||
- Now you can work on the `ValidateNumberOfCows` function. | ||||||
Depending on the number of cows ([if-statement][concept-conditionals]), it should create and return a new instance of the `InvalidCowsError` and set the correct message while doing so. | ||||||
If the number of cows was valid, `nil` should be returned. | ||||||
|
||||||
[concept-methods]: /tracks/go/concepts/methods | ||||||
[concept-functions]: /tracks/go/concepts/functions | ||||||
[concept-numbers]: /tracks/go/concepts/numbers | ||||||
[concept-structs]: /tracks/go/concepts/structs | ||||||
[concept-conditionals]: /tracks/go/concepts/conditionals-if | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.