Menu
CoddyTech

Validation with Setters

Part of the Object Oriented Programming section of Coddy's GDScript journey. Lesson 27 of 65.

A setter runs every time its property is assigned, which makes it the place to check the new value. Python raises an error for a bad value. GDScript has no exceptions, so a setter does one of these instead: it clamps the value into range, or it keeps the old value and records what went wrong.

# character.gd
class_name Character
extends RefCounted

var last_error: String = ""
var hp: int = 100:
	set(value):
		hp = clampi(value, 0, 100)
var nickname: String = "Player":
	set(value):
		if value.length() < 3:
			last_error = "Name too short"
			return
		last_error = ""
		nickname = value

clampi(value, 0, 100) returns value moved into the range 0 to 100. In the nickname setter, return leaves before the assignment, so the old name stays and last_error explains why:

# main.gd
var hero := Character.new()
hero.hp = 250
print(hero.hp) # 100
hero.nickname = "Al"
print(hero.nickname) # Player
print(hero.last_error) # Name too short
hero.nickname = "Alice"
print(hero.nickname) # Alice

The setter also runs for assignments inside the class, in _init() and in every method. Assign the constructor's arguments through the property and they are checked as well:

func _init(start_hp: int) -> void:
	hp = start_hp # runs the setter: Character.new(500) starts with 100

Inside the setter itself, assigning hp stores the value without calling the setter again. The one assignment that skips the setter is the initial value in the declaration (= 100), so that value must be valid on its own.

challenge icon

Challenge

Easy

Add validation to the Thermostat class in thermostat.gd with setters:

  • target (starts at 20) accepts only values from 10 to 30. For any other value it keeps the old target and sets last_error to Target must be between 10 and 30.
  • mode (starts at off) accepts only heat, cool and off. For any other text it keeps the old mode and sets last_error to Unknown mode: followed by the text.
  • A valid assignment sets last_error back to an empty string.
  • _init(start_target) assigns start_target to target, so the setter checks it.

The supplied code reads the starting target, then the number of commands, then one command per line (target 25 or mode cool). After creating the thermostat and after each command it prints Rejected: and the error when last_error is not empty, then the state. For the start 22 and the commands target 35 and mode cool it prints:

Target 22, mode off
Rejected: Target must be between 10 and 30
Target 22, mode off
Target 22, mode cool

Your code goes in thermostat.gd. main.gd holds the supplied input/output code and cannot be edited.

Try it yourself

extends Node

func _ready():
	# Supplied input/output code: keep it as it is
	var thermostat := Thermostat.new(int(OS.read_string_from_stdin()))
	report(thermostat)
	var count := int(OS.read_string_from_stdin())
	for i in count:
		var parts := OS.read_string_from_stdin().split(" ")
		if parts[0] == "target":
			thermostat.target = int(parts[1])
		else:
			thermostat.mode = parts[1]
		report(thermostat)

func report(thermostat: Thermostat) -> void:
	if thermostat.last_error != "":
		print("Rejected: " + thermostat.last_error)
	print("Target %d, mode %s" % [thermostat.target, thermostat.mode])
quiz iconTest yourself

This lesson includes a short quiz. Start the lesson to answer it and track your progress.

All lessons in Object Oriented Programming

Practice on your own: Online GDScript compiler