Recap - Safe Division
Part of the Logic & Flow section of Coddy's GO journey — lesson 44 of 68.
Challenge
EasyBuild a comprehensive mathematical calculator that implements safe division with multiple custom error types and proper error handling. This challenge combines custom error types, error wrapping, and error checking to create a robust system that handles various mathematical operation failures gracefully.
You will receive two inputs:
- A string containing operation details in the format
"operation,num1,num2,precision"(e.g.,"divide,10,0,2") - A string containing calculator settings in the format
"calculator_name,mode"(e.g.,"ScientificCalc,strict")
Your task is to:
- Create two custom error types:
DivisionErrorstruct with fieldsDividend(float64) andDivisor(float64)ValidationErrorstruct with fieldsField(string) andValue(float64)
- Implement the
Error() stringmethod forDivisionErrorthat returns:"division error: cannot divide [dividend] by [divisor]" - Implement the
Error() stringmethod forValidationErrorthat returns:"validation error: invalid [field] value [value]" - Parse the first input by splitting on commas to get operation, num1, num2, and precision
- Parse the second input by splitting on commas to get calculator name and mode
- Convert the string numbers to float64 and precision to int
- Create a
safeDividefunction that takes two float64 parameters and returns (float64, error):- If divisor is 0, return 0 and a
DivisionErrorwith the dividend and divisor values - If dividend is negative and mode is
"strict", return 0 and aValidationErrorwith field"dividend"and the dividend value - If divisor is negative and mode is
"strict", return 0 and aValidationErrorwith field"divisor"and the divisor value - Otherwise, return the division result and nil
- If divisor is 0, return 0 and a
- Create a
performCalculationfunction that wraps thesafeDivideresult:- If
safeDividereturns an error, wrap it with:"calculation failed in [calculator_name]: %w" - If successful, return the result and nil
- If
- Call
performCalculationand handle the result:- If no error: print
"Calculation successful: [result_formatted_to_precision_decimal_places]" - If error exists: print
"Calculation failed: [error_message]"
- If no error: print
- Use
errors.Asto check for specific error types in the wrapped error:- Print
"Checking for division error: [true/false]" - Print
"Checking for validation error: [true/false]"
- Print
- If a
DivisionErroris found, print its details:"Division Error Details:""Dividend: [dividend]""Divisor: [divisor]"
- If a
ValidationErroris found, print its details:"Validation Error Details:""Field: [field]""Value: [value]"
- Display a final summary:
"Calculator Summary:""Name: [calculator_name]""Mode: [mode]""Operation: [operation]""Input: [num1] [operation_symbol] [num2]"where operation_symbol is "/" for divide"Precision: [precision] decimal places""Status: [Success/Failed]"
Use the strings package to split the input strings on commas, the strconv package to convert strings to numbers, the errors package for errors.As, and the fmt package for error wrapping and formatting. This challenge demonstrates how custom error types, error wrapping, and proper error checking work together to create robust error handling systems that provide detailed information about different types of failures.
Try it yourself
package main
import (
"errors"
"fmt"
"strconv"
"strings"
)
func main() {
// Read input
var operationInput string
var settingsInput string
fmt.Scanln(&operationInput)
fmt.Scanln(&settingsInput)
// Parse operation input (operation,num1,num2,precision)
operationParts := strings.Split(operationInput, ",")
operation := operationParts[0]
num1, _ := strconv.ParseFloat(operationParts[1], 64)
num2, _ := strconv.ParseFloat(operationParts[2], 64)
precision, _ := strconv.Atoi(operationParts[3])
// Parse settings input (calculator_name,mode)
settingsParts := strings.Split(settingsInput, ",")
calculatorName := settingsParts[0]
mode := settingsParts[1]
// TODO: Write your code below
// 1. Create DivisionError and ValidationError structs with Error() methods
// 2. Implement safeDivide function
// 3. Implement performCalculation function
// 4. Call performCalculation and handle results
// 5. Use errors.As to check for specific error types
// 6. Print error details if found
// 7. Display final summary
}All lessons in Logic & Flow
1Advanced Control Flow
Switch with `fallthrough`Breaking from Nested LoopsContinuing a Specific LoopThe `goto` StatementRecap - Advanced Loop Control4Project: Simple Task List
Project SetupAdding a Task7Error Handling In-Depth
Custom Error TypesWrapping Errors with `%w`Unwrapping with `errors.Is`Unwrapping with `errors.As`Understanding `panic`Using `recover`Recap - Safe Division2Structs and Methods
Defining Methods on StructsValue ReceiversPointer ReceiversChoosing ReceiversMethods vs FunctionsRecap - Struct Behavior5Maps In-Depth
Maps of StructsPointers as Map ValuesTesting for Nil MapsComparing MapsRecap - Word Frequency Counter3Interfaces (The Basics)
What is an Interface?Defining an InterfaceImplementing an InterfaceUsing Interface TypesEmpty InterfaceType AssertionsType SwitchRecap - Shapes and Behaviors