A calculator is a great way to practise functions that return two values: a
result and an error. In Go, reporting failure with an error instead of a panic
is the idiomatic way to handle bad input.
Statement
Write a function calc(a, b int, op string) (int, error) that applies the
operator op to a and b. Support +, -, * and /. Return an error
when the operator is unknown, or when a division by zero is attempted.
Constraints:
- The signature must be
func calc(a, b int, op string) (int, error). - On success, return the result and a
nilerror. - On failure, return
0and a non-nil error; never panic. - Check
b == 0before dividing.
Example
calc(12, 4, "+") -> 16, <nil>
calc(12, 4, "/") -> 3, <nil>
calc(1, 0, "/") -> 0, division by zero
calc(1, 2, "%") -> 0, unknown operator: "%"
Starter code
package main
import (
"errors"
"fmt"
)
// calc applies op ("+", "-", "*", "/") to a and b.
func calc(a, b int, op string) (int, error) {
// Return the result, or an error for a division by zero
// and for an unknown operator.
return 0, nil
}
func main() {
result, err := calc(12, 4, "+")
fmt.Println(result, err)
}
Hints
Hint 1
A switch op { ... } gives you one clean branch per operator, plus a default
for everything else.
Hint 2
In the "/" branch, check if b == 0 first and return an error such as
errors.New("division by zero") before you divide.
Hint 3
Let the default case return fmt.Errorf("unknown operator: %q", op) so the
caller sees which operator was rejected.
Solution
Show the solution
package main
import (
"errors"
"fmt"
)
var errDivideByZero = errors.New("division by zero")
func calc(a, b int, op string) (int, error) {
switch op {
case "+":
return a + b, nil
case "-":
return a - b, nil
case "*":
return a * b, nil
case "/":
if b == 0 {
return 0, errDivideByZero
}
return a / b, nil
default:
return 0, fmt.Errorf("unknown operator: %q", op)
}
}
func main() {
for _, op := range []string{"+", "-", "*", "/"} {
result, err := calc(12, 4, op)
if err != nil {
fmt.Printf("12 %s 4 -> error: %v\n", op, err)
continue
}
fmt.Printf("12 %s 4 = %d\n", op, result)
}
if _, err := calc(1, 0, "/"); err != nil {
fmt.Printf("1 / 0 -> error: %v\n", err)
}
}calc returns two values, which is the standard Go pattern: the result and an
error that is nil when everything went well. The switch picks one branch
per operator. The "/" branch guards against a zero divisor before dividing, so
the program returns a clear error instead of panicking. The caller checks
err != nil and reacts before trusting the result.
Common mistakes:
- Dividing before checking
b == 0, which triggers a runtime panic. - Returning
nilfor the error but a meaningless result, so the caller cannot tell success from failure. - Ignoring the error at the call site with
result, _ := calc(...), which hides real problems.
Dig into the mechanics of parameters and return values in Functions in Go.