See https://github.com/dotnet/csharplang/blob/master/spec/structs.md
Record structs cannot use `ref` modifier.
At most one partial type declaration of a partial record struct may provide a `parameter_list`.
The `parameter_list` may not be empty.
Record struct parameters cannot use `ref`, `out` or `this` modifiers (but `in` and `params` are allowed).
## Members of a record struct
In addition to the members declared in the record struct body, a record struct type has additional synthesized members.
Members are synthesized unless a member with a "matching" signature is declared in the record struct body or
an accessible concrete non-virtual member with a "matching" signature is inherited.
Two members are considered matching if they have the same
signature or would be considered "hiding" in an inheritance scenario.
See https://github.com/dotnet/csharplang/blob/master/spec/basic-concepts.md#signatures-and-overloading
It is an error for a member of a record struct to be named "Clone".
It is an error for an instance field of a record struct to have an unsafe type.
A record struct is not permitted to declare a destructor.
The synthesized members are as follows:
### Equality members
The synthesized equality members are similar as in a record class (`Equals` for this type, `Equals` for `object` type, `==` and `!=` operators for this type),\
except for the lack of `EqualityContract`, null checks or inheritance.
The record struct implements `System.IEquatable<R>` and includes a synthesized strongly-typed overload of `Equals(R other)` where `R` is the record struct.
The method is `public`.
The method can be declared explicitly. It is an error if the explicit declaration does not match the expected signature or accessibility.
If `Equals(R other)` is user-defined (not synthesized) but `GetHashCode` is not, a warning is produced.
```C#
public bool Equals(R other);
```
The synthesized `Equals(R)` returns `true` if and only if for each instance field `fieldN` in the record struct
the value of `System.Collections.Generic.EqualityComparer<TN>.Default.Equals(fieldN, other.fieldN)` where `TN` is the field type is `true`.
The record struct includes synthesized `==` and `!=` operators equivalent to operators declared as follows:
```C#
public static bool operator==(R r1, R r2)
=> r1.Equals(r2);
public static bool operator!=(R r1, R r2)
=> !(r1 == r2);
```
The `Equals` method called by the `==` operator is the `Equals(R other)` method specified above. The `!=` operator delegates to the `==` operator. It is an error if the operators are declared explicitly.
The record struct includes a synthesized override equivalent to a method declared as follows:
```C#
public override bool Equals(object? obj);
```
It is an error if the override is declared explicitly.
The synthesized override returns `other is R temp && Equals(temp)` where `R` is the record struct.
The record struct includes a synthesized override equivalent to a method declared as follows:
```C#
public override int GetHashCode();
```
The method can be declared explicitly.
A warning is reported if one of `Equals(R)` and `GetHashCode()` is explicitly declared but the other method is not explicit.
The synthesized override of `GetHashCode()` returns an `int` result of combining the values of `System.Collections.Generic.EqualityComparer<TN>.Default.GetHashCode(fieldN)` for each instance field `fieldN` with `TN` being the type of `fieldN`.
1. for each of the record struct's printable members (non-static public field and readable property members), appends that member's name followed by " = " followed by the member's value separated with ", ",
2. return true if the record struct has printable members.
For a member that has a value type, we will convert its value to a string representation using the most efficient method available to the target platform. At present that means calling `ToString` before passing to `StringBuilder.Append`.
The `PrintMembers` method can be declared explicitly.
It is an error if the explicit declaration does not match the expected signature or accessibility.
The record struct includes a synthesized method equivalent to a method declared as follows:
```C#
public override string ToString();
```
The method can be declared explicitly. It is an error if the explicit declaration does not match the expected signature or accessibility.
The synthesized method:
1. creates a `StringBuilder` instance,
2. appends the record struct name to the builder, followed by " { ",
3. invokes the record struct's `PrintMembers` method giving it the builder, followed by " " if it returned true,
4. appends "}",
5. returns the builder's contents with `builder.ToString()`.
For example, consider the following record struct:
``` csharp
record struct R1(T1 P1, T2 P2);
```
For this record struct, the synthesized printing members would be something like:
```C#
struct R1 : IEquatable<R1>
{
public T1 P1 { get; set; }
public T2 P2 { get; set; }
private bool PrintMembers(StringBuilder builder)
{
builder.Append(nameof(P1));
builder.Append(" = ");
builder.Append(this.P1); // or builder.Append(this.P1.ToString()); if P1 has a value type
builder.Append(", ");
builder.Append(nameof(P2));
builder.Append(" = ");
builder.Append(this.P2); // or builder.Append(this.P2.ToString()); if P2 has a value type
return true;
}
public override string ToString()
{
var builder = new StringBuilder();
builder.Append(nameof(R1));
builder.Append(" { ");
if (PrintMembers(builder))
builder.Append(" ");
builder.Append("}");
return builder.ToString();
}
}
```
## Positional record struct members
In addition to the above members, record structs with a parameter list ("positional records") synthesize
additional members with the same conditions as the members above.
### Primary Constructor
A record struct has a public constructor whose signature corresponds to the value parameters of the
type declaration. This is called the primary constructor for the type. It is an error to have a primary
constructor and a constructor with the same signature already present in the struct.
A record struct is not permitted to declare a parameterless primary constructor.
- confirm that we want to keep PrintMembers design (separate method returning `bool`) (answer: yes)
- confirm we won't allow `record ref struct` (issue with `IEquatable<RefStruct>` and ref fields) (answer: yes)
- confirm implementation of equality members. Alternative is that synthesized `bool Equals(R other)`, `bool Equals(object? other)` and operators all just delegate to `ValueType.Equals`. (answer: yes)
- confirm that we want to allow field initializers when there is a primary constructor. Do we also want to allow parameterless struct constructors while we're at it (the Activator issue was apparently fixed)? (answer: yes, updated spec should be reviewed in LDM)
- how much do we want to say about `Combine` method? (answer: as little as possible)