union (a sum type)
Forms that compile (8)
Section titled “Forms that compile (8)”| Syntax | Description |
|---|---|
abstract record Shape; record Circle(float R) : Shape; | Declaration and value generation |
s switch { Circle c => ..., _ => ... } | Branching by type |
s switch { Circle c => ..., Rect r => ... } | Branching without a default case |
A a => ..., B b => ..., C c => ... | Branching over 3 or more types |
s is Circle | Testing for a specific type |
void Use(Shape s) | Receiving as a parameter |
Shape Make() | Returning as a return value |
Shape[] all = new Shape[2]; | Array element |
Forms that don’t compile (3)
Section titled “Forms that don’t compile (3)”| Syntax | Description | Error | Reason | Instead |
|---|---|---|---|---|
private Shape held; | Holding as a field | TUKI0001 | not yet | Pass it via a local variable, argument, or array |
record Pair(Node L, Node R) : Node; | A definition that includes itself (recursion) | TUKI0001 | runtime | Represent a tree with an array and indices |
Debug.Log(s) | Passing a union value out as is | TUKI0001 | runtime | Branch by type first, then pass the extracted value |
| A union value can’t be held in a field. To keep it as state, put the enum value that represents the kind and each type’s contents in separate fields. |
An interface attached to a value type (struct / record) is converted to this same form. Both become a single number that represents which type is currently held, plus storage for each type, laid out in sequence. Only the way it’s written differs; the underlying mechanism is the same. Only an interface attached to a Behaviour uses a different mechanism — it points to the Behaviour itself.
Each type’s storage is separate; they don’t overlap. The number of slots a union uses is the sum of every type’s contents, plus one for the number that represents the kind. It isn’t sized to match the largest type. Adding one more type increases the count by that type’s contents (for the slot limit, see Udon’s execution model).
Declaration and branching
Section titled “Declaration and branching”Declaration and value generation
Section titled “Declaration and value generation”Declare the base with abstract record, and declare each type with a record that inherits from that base.
using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnDeclare : TsukimiBehaviour{ void Start() { Shape s = new Circle(1f); float a = s switch { Circle c => c.R, Rect r => r.W * r.H, _ => 0f, }; Debug.Log(a); // => 1 }}Branching by type
Section titled “Branching by type”using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnSwitchWithDefault : TsukimiBehaviour{ void Start() { Shape s = new Rect(2f, 3f); float a = s switch { Circle c => c.R, Rect r => r.W * r.H, _ => 0f, }; Debug.Log(a); // => 6 }}Branching without a default case
Section titled “Branching without a default case”If the cases are exhaustive, you don’t need a default case.
using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnSwitchExhaustive : TsukimiBehaviour{ void Start() { Shape s = new Circle(1f); float a = s switch { Circle c => c.R, Rect r => r.W * r.H, }; Debug.Log(a); // => 1 }}Branching over 3 or more types
Section titled “Branching over 3 or more types”using UnityEngine;using Tsukimi;
public abstract record Shape;public record A(int N) : Shape;public record B(int N) : Shape;public record C(int N) : Shape;
public class UnThreeCases : TsukimiBehaviour{ void Start() { Shape s = new A(1); int v = s switch { A a => a.N, B b => b.N, C c => c.N, _ => 0 }; Debug.Log(v); // => 1 }}Testing for a specific type
Section titled “Testing for a specific type”using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnIsPattern : TsukimiBehaviour{ void Start() { Shape s = new Circle(1f); bool isCircle = s is Circle; Debug.Log(isCircle); // => true }}Storage
Section titled “Storage”Receiving as a parameter
Section titled “Receiving as a parameter”using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnParameter : TsukimiBehaviour{ void Start() { Use(new Circle(1f)); }
private void Use(Shape s) { float a = s switch { Circle c => c.R, Rect r => r.W * r.H, _ => 0f, }; Debug.Log(a); // => 1 }}Returning as a return value
Section titled “Returning as a return value”using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnReturnValue : TsukimiBehaviour{ void Start() { Shape s = Make(); float a = s switch { Circle c => c.R, Rect r => r.W * r.H, _ => 0f, }; Debug.Log(a); // => 1 }
private Shape Make() { return new Circle(1f); }}Array element
Section titled “Array element”using UnityEngine;using Tsukimi;
public abstract record Shape;public record Circle(float R) : Shape;public record Rect(float W, float H) : Shape;
public class UnArray : TsukimiBehaviour{ void Start() { Shape[] all = new Shape[2]; all[0] = new Circle(1f); all[1] = new Rect(1f, 2f); float total = 0f; for (int i = 0; i < all.Length; i++) { Shape s = all[i]; float a = s switch { Circle c => c.R, Rect r => r.W * r.H, _ => 0f, }; total += a; } Debug.Log(total); // => 3 }}