Osy#betaRun it on Osyrin: osyrin.com

Reference / Class

Structs

struct Name { … } — a value type: copied where it is stored

A `struct` is a VALUE type, as in C#: assigning one, passing one, returning one or putting one in a list stores a COPY, so a change through one name is never seen through another. An unset struct — a field, a local — is its default, every member zero, never null. It compares by its members through `Equals`; a `record struct` adds `==`.

preview3 examples compiled by CIclasstypesauthoring

Summary#

A struct copies where a class would share:

struct Point { public int X; public int Y; }

int Copy() {
  Point a = new Point();
  a.X = 1;
  Point b = a;      // a copy
  b.X = 2;
  return a.X;       // 1 — `a` was never touched
}

Signature#

struct Name { public int X; public Name(int x) { X = x; } }   // fields, constructors, methods — as a class
readonly struct Name { … }                                     // C#'s promise that it is not changed after it is made
record struct Name(int X, int Y);                              // a struct with a record's members, == included
Name n;          // a struct local is its DEFAULT, every member zero — `n.X = 3;` may follow
a.Equals(b)      // member by member

Description#

Where the copy happens#

Everywhere a struct is STORED: a local (Point b = a;), an assignment, an argument (the callee changes its own copy), a returned value, a field of another type, and an element of a collection (points.Add(p) adds a copy). Writing a member through the place that holds it changes that place — a.X = 1;, holder.Position.X = 5; — which is what makes a struct field of a class usable at all.

Its default#

A struct is never null. A field of a struct type with no initializer starts at the struct's default — every member at its own default — and so does a local declared without a value. Point? p; is the spelling for "maybe none".

Equality#

a.Equals(b) compares every member, and Distinct, Contains and a dictionary key use the same rule. A plain struct has no ==, as in C# — write Equals, or declare it a record struct, which has both.

readonly#

readonly struct and a struct's readonly method (public readonly int Sum() => X + Y;) are accepted where C# accepts them. Every call already reads the value as it stands, so neither changes an answer.

Not a ref struct#

A ref struct is confined to the stack, and an Osy# body can suspend and resume later, on another machine — there is no stack to confine it to. It is refused by name; declare a struct.

Examples#

struct Point { public int X; public int Y; }
class Shape { public Point Origin; }

int Place() {
  var s = new Shape();          // Origin is (0, 0), not null
  s.Origin.X = 5;               // writes the field itself
  var list = new List<Point>();
  list.Add(s.Origin);           // a copy goes into the list
  s.Origin.X = 9;
  return list[0].X;             // 5
}
record struct Pos(int X, int Y);

bool Same() {
  var p = new Pos(1, 2);
  return p == new Pos(1, 2) && (p with { Y = 3 }).Y == 3;   // true
}

Errors#

MessageCauseFix
`==` is not defined on the struct 'X'A plain struct has no ==, as in C#.Write a.Equals(b), or declare it record struct X.
Osy# has no `ref struct`A stack-only type has no stack to live on in a durable body.Declare a struct.
`struct X(…)` — a struct's parameter list is a primary constructorA plain struct with a parameter list.Write record struct X(…), or declare the fields and a constructor.

See also#

Related

Records

A `record` is a class whose values compare by their MEMBERS rather than by identity, exactly as a C# record does. The…

Comparing classes

`==` on two `class` values asks whether they are the SAME INSTANCE, exactly as C# does: two objects built separately…

Classes

A class is an in-memory shape — data plus the behaviour that belongs to it — and it never touches the database. That is…

Copying a class with changes

`with` makes a COPY of a class value and replaces the fields you name. Everything you do not name comes from the value…

`readonly` fields

A `readonly` field can be assigned only where it is declared or in a constructor of the class that declares it…