Let ≡n
be the equivalence class of nominally equivalent types.
A ≡n B
denotes that A
and B
are nominally the same type.
{ foo: T, bar: U } ≡n { bar: U, foo: T } // Order does not matter.
{ foo: T, bar: U } ≢n { foo: T, bar: X } // All fields must have the same types.
{ foo: T, bar: U } ≢n { foo: T, quux: U } // The name of the fields must be the same.
You can coerce { foo: T, bar: U }
to { foo: T, }
or { bar: U, }
.
Such a coercion performs a partial move.
struct NC;
let source: { v: NC, x: String, y: u8, z: u16 }
= { v: NC, x: "foo".into(), y: 2, z: 3 };
let target: { x: String, y: u8 } = source; // OK! Partial move
drop(source.v); // OK! since partial move and 'v' was not moved.
drop(source.z);
However, &{ foo: T, bar: U }
does not coerce to &{ foo: T, }
or &{ bar: U, }
.
This also applies to &mut
, *mut
and *const
.
You can coerce unnamed structs to named ones. An example:
struct Foo { x: u8, y: u8 }
let bar = { x: 1, y: 2 };
let foo: Foo = bar;
FIXME: This seems hazardous and bad for understanding of code.
You can coerce a named struct into an unnamed one. An example:
fn take_xyz({x, y, z}: {x: u8, y: u8, z: u8}) { ... }
struct Foo { x: u8, y: u8, z: u8 }
take_xyz(Foo { x: 1, y: 2, z: 3})
The privacy of the named struct must be respected; you may only do this if the fields referenced in the unnamed struct and the named struct are visible in the given context.
mod alpha {
struct Foo { pub x: u8, y: u8 }
let foo = Foo { x: 1, y: 2 };
let bar: { x: u8, y: u8 } = foo; // OK!
}
mod beta {
let wibble: { x: u8, } = foo; // OK! (foo is from above)
let wobble: { y: u8, } = foo; // ERR! Foo.y is not visible to 'beta'.
}
The type { foo: T, bar: U }
is not a subtype of { foo: T, }
nor { bar: U, }
!
It is not a subtype because it does not fit with Rust's memory model.
This should not be a problem since you can always use coercions.
All subtyping in Rust also flows from lifetimes, so this would be terribly inconsistent.
You can use FRU syntax on unnamed structs like so:
let alpha = { a: 1, b: 2 };
let beta = { a: 3, ..alpha };
let gamma = { b: 4, ..alpha }
assert_eq!(beta, { a: 3, b: 2 });
assert_eq!(gamma, { a: 1, b: 4 });
You can expand an unnamed struct with more fields like so:
let alpha = { a: 1, b: 2 };
let beta = { c: 3, b: 0, ..alpha };
assert_eq!(beta, { a: 1, b: 0, c: 3});
You can merge two unnamed structs like so:
let alpha = { a: 1, b: 2 };
let beta = { c: 3, d: 4 };
let gamma = { ..alpha, ..beta };
assert_eq!(gamma, { a: 1, d: 2, c: 3, d: 4 });
The semantics of merging two unnamed structs is well defined as updating left to right such that the value is taken from the right-most unnamed field when there is overlap. An example:
assert_eq!( { ..{ a: 1, b: 2 }, ..{ b: 3, c: 4 } }
, { a: 1, b: 3, c: 4 } );
You can update a named struct or even create one with FRU provided privacy checks out.
struct Foo { x: u8, y: u8 }
let bar = { x: 1, y: 2 };
let foo1 = Foo { ..bar }; // Create Foo from 'bar'.
let foo2 = Foo { x: 3, ..bar } // Update it from 'bar'.
How do you write the named 1-tuple? Like so:
{ field: type, } // type level
{ field: value, } // value level literal
Note in particular the ,
at the end.
This is required, because you'd otherwise get an ambiguity with type ascription.
The syntax struct { field: type, .. }
is not used since it is inconsistent
with normal tuples.
To make the system useful, the following impls are provided lazily:
[forall field. typeof(field) : Trait)] => Trait
where Trait
is one of
Clone
,Copy
,Default
,Debug
,Hash
,PartialEq
,Eq
,PartialOrd
,Ord
.- auto traits
Other possible impls:
Add
, ..
Note that these impls are not expressible in Rust itself since the language gives no way to quantify over fields and their types.
Simply put, you may only implement traits for unnamed structs iff the trait is defined in your module. This restriction exists to preserve coherence.
Very nice!
Would it be safe to allow coercion from unnamed structs to named ones if there's a structural match?