Calculator in Go

Build a small integer calculator that returns a result or an error for each operation, then compare it with a complete tested Go solution.

Level
Medium
Estimated time
20 min
Concepts covered
fonctions, erreurs

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 nil error.
  • On failure, return 0 and a non-nil error; never panic.
  • Check b == 0 before 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 nil for 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.

Search

Search runs entirely in your browser.