Join GitHub today
GitHub is home to over 40 million developers working together to host and review code, manage projects, and build software together.
Sign upRFC: Structural Records #2584
RFC: Structural Records #2584
Conversation
This comment has been minimized.
This comment has been minimized.
|
This looks like a super well thought out and comprehensive RFC. It might be worth clarifying the behaviour of struct RectangleTidy {
dimensions: {
width: u64,
height: u64,
},
color: {
red: u8,
green: u8,
blue: u8,
},
}Presumably, there will be no "magic" here, and you will only be able to derive traits which are implemented for the anonymous structs themselves. Another question is around trait implementations: at the moment, 3rd party crates can provide automatic implementations of their traits for "all" tuples by using macro expansion to implement them for 0..N element tuples. With anonymous structs this will not be possible. Especially with the likely arrival of const generics in the not too distant future, negating the need for macros entirely, anonymous structs will become second class citizens. Is this a problem, and are there any possible solutions to allow implementing traits for all anonymous structs? |
| other_stuff(color.1); | ||
| ... | ||
| yet_more_stuff(color.2); | ||
| } |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Nov 2, 2018
Contributor
fn do_stuff_with((red, green, blue): (u8, u8, u8)) {
some_stuff(red);
other_stuff(green);
yet_more_stuff(blue);
}
This comment has been minimized.
This comment has been minimized.
Centril
Nov 2, 2018
•
Author
Member
Even better, with #2522:
fn do_stuff_with((red: u8, green: u8, blue: u8)) {
some_stuff(red);
other_stuff(green);
yet_more_stuff(blue);
}However, while if you write it in this way it is clear what each tuple component means, it is not clear at the call site...
let color = (255, 0, 0);
// I can guess that this is (red, green, blue) because that's
// the usual for "color" but it isn't clear in the general case.| blue: u8, | ||
| }, | ||
| } | ||
| ``` |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Nov 2, 2018
Contributor
This syntax looks very similar to another already accepted RFC, but semantics seems different - #2102.
This comment has been minimized.
This comment has been minimized.
Centril
Nov 2, 2018
Author
Member
True; I have to think about if some unification can be done in some way here.
This comment has been minimized.
This comment has been minimized.
Thanks!
This bit is not currently well specified, but it should be so I will fix that (EDIT: fixed)... I see two different ways to do it:
Yup. Usually up to 12, emulating the way the standard library does this.
Nit: to not have to use macros for implementing traits for tuples you need variadic generics, not const generics. :)
I think it might be possible technically; I've written down some thoughts about it in the RFC. However, the changes needed to make it possible might not be what folks want. However, not solving the issue might also be good; by not solving the issue you add a certain pressure to gradually move towards nominal typing once there is enough operations and structure that you want on the type. |
…it)].
| standard traits that are implemented for [tuples]. These traits are: `Clone`, | ||
| `Copy`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `Debug`, `Default`, and `Hash`. | ||
| Each of these traits will only be implemented if all the field types of a struct | ||
| implements the trait. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Centril
Nov 3, 2018
Author
Member
Ideas for possible magic reduction discussed below in #2584 (comment).
| + For `PartialEq`, each field is compared with same field in `other: Self`. | ||
|
|
||
| + For `ParialOrd` and `Ord`, lexicographic ordering is used based on | ||
| the name of the fields and not the order given because structural records |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Nov 2, 2018
Contributor
With macros fields can have names with same textual representation, but different hygienic contexts.
This comment has been minimized.
This comment has been minimized.
Centril
Nov 2, 2018
Author
Member
Does that change anything wrt. the implementations provided tho? I don't see any problems with hygiene intuitively, but maybe I can given elaboration?
This comment has been minimized.
This comment has been minimized.
petrochenkov
Nov 2, 2018
•
Contributor
This is probably relevant to other places where the order of fields needs to be "normalized" as well.
This comment has been minimized.
This comment has been minimized.
Centril
Nov 2, 2018
Author
Member
Sure. :) But I'm not sure if you are pointing out a problem or just noting...
Any sorting / "normalization" is done post expansion.
This comment has been minimized.
This comment has been minimized.
petrochenkov
Nov 2, 2018
Contributor
It's not clear to me how to sort hygienic contexts in stable order, especially in cross-crate scenarios. That probably can be figured out somehow though.
| ```rust | ||
| ty ::= ... | ty_srec ; | ||
| ty_srec ::= "{" (ty_field ",")+ (ty_field ","?)? "}" ; |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Nov 2, 2018
•
Contributor
I don't think types can start with { syntactically.
There are multiple already existing ambiguities where parser assumes that types cannot start with {, and upcoming const generics introduce one more big ambiguity ({} is used to disambiguate in favor of const generic arguments).
#2102 uses struct { field: Type, ... } to solve this (and also to discern between struct { ... } and union { ... }).
This comment has been minimized.
This comment has been minimized.
petrochenkov
Nov 2, 2018
Contributor
I'm not sure about expressions/patterns.
Unlimited lookahead may also be required, need to check more carefully.
This comment has been minimized.
This comment has been minimized.
Centril
Nov 2, 2018
Author
Member
There are multiple already existing ambiguities where parser assumes that types cannot start with
{
Such as? (examples please...)
Const generics allow literals and variables as expressions but anything else needs to be in { ... }.
This is not ambiguous with structural records (because the one-field-record requires a comma...) but requires lookahead.
Unlimited lookahead may also be required, need to check more carefully.
Scroll down ;) It's discussed in the sub-section "Backtracking".
This comment has been minimized.
This comment has been minimized.
petrochenkov
Nov 2, 2018
Contributor
It's discussed in the sub-section "Backtracking".
It's a huge drawback.
This comment has been minimized.
This comment has been minimized.
petrochenkov
Nov 2, 2018
Contributor
Such as? (examples please...)
Nothing that can't be solved by infinite lookahead, but here's "how to determine where where clause ends":
fn f() where PREDICATE1, PREDICATE2, { a: ...
Does { a: ... belong to the function body or to the third predicate?
This comment has been minimized.
This comment has been minimized.
Centril
Nov 7, 2018
Author
Member
@Laaas Because it is inconsistent with Tuples/Tuple-structs and also less ergonomic.
I don't think it's confusing for readers, the one-field case is clearly disambiguated by an extra comma and that is consistent with how tuples are treated. In other words, if { x, } is confusing, then so is (x,).
This comment has been minimized.
This comment has been minimized.
GrayJack
Jan 10, 2020
If it's going to be drawbacks of this pattern for the structural records, either for slowing parser or cause types can't start with { syntactically, couldn't we use .{ instead?
Inspiration by Zig programming language
This comment has been minimized.
This comment has been minimized.
Centril
Jan 10, 2020
•
Author
Member
Actually, I've been thinking about this recently and I've come to the conclusion that if we lean into the ambiguity, then this is an non-issue.
The need for backtracking here stems from ambiguity with type ascription. This can be fixed by simply assuming that { ident: is always going to be a structural record, either in expression or type position (also solves the const generics issue). That is, if given { foo: Vec<u8> } in an expression context, we will attempt parsing this as a structural record, and then fail at < with a parse error (backtracking can be done inside diagnostics only if we want to). This way, we have retained LL(k) (standing at {, the : is 2 tokens away and so therefore k = 2 in this instance).
If a user wants to disambiguate in favor of ascription, they can write { (ident: Type) }. Disambiguating in this way should not be a problem as { ident: Type } is not a very useful thing to write. Specifically, in the case of PREDICATE2, { a: ..., this should not be a problem because the return type itself is a coercion site and informs type inference.
This comment has been minimized.
This comment has been minimized.
comex
Jan 11, 2020
Why would it fail at <? If you're trying to parse Vec<u8> as an expression, wouldn't the < be parsed as less-than, and you only fail when you see > (and in general arbitrarily far away)?
It seems like the same issue as when we tried to get rid of turbofish...
This comment has been minimized.
This comment has been minimized.
Centril
Jan 11, 2020
Author
Member
@comex Oh; yeah, you're right. It should fail at >. Still, we can take a snapshot before trying to parse the expression after ident: and backtrack if it turned out to be a type and provide good MachineApplicable diagnostics here.
It seems like the same issue as when we tried to get rid of turbofish...
Well in the sense that turbofish would also be unnecessary if we are OK with backtracking, but it seems like some people object to backtracking, so I've come up with a solution using LL(k) at the cost of not being able to recognize { binding: Type } as a block with a tail-expression ascribing binding. Because such code is, in my view, pathological, that should be fine. It's much less of an issue than turbofish from a learnability POV imo.
| field_init ::= ident | field ":" expr ; | ||
| ``` | ||
|
|
||
| Note that this grammar permits `{ 0: x, 1: y }`. |
This comment has been minimized.
This comment has been minimized.
petrochenkov
Nov 2, 2018
Contributor
#1595 was closed, so I'd expect this to be rejected.
This saves us from stuff like "checking that there are no gaps in the positional fields" as well.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
varkor
Nov 3, 2018
Member
struct A(u8);
let _ = A { 0: 0u32 };is accepted, so this is currently inconsistent as it stands anyway.
This comment has been minimized.
This comment has been minimized.
Centril
Nov 4, 2018
•
Author
Member
@varkor yeah that was accepted to make macro writing easier (and it totally makes it easier...!)
This comment has been minimized.
This comment has been minimized.
|
I think the language should be frozen for large additions like this in general, for a couple of years at least. |
This comment has been minimized.
This comment has been minimized.
|
The trait auto-implementation magic worries me, mostly from the point of view of things not the compiler wanting to implement traits on things. Today crates can do a macro-based implementation for the important tuples, and I can easily see a path to variadic generics where the impls could easily support any tuple. But I can imagine I'd also be tempted to block this on changing struct literal syntax to using Overall, I think I feel that this is cool, but not necessary. |
This comment has been minimized.
This comment has been minimized.
|
I'm very concerned with properties of the struct explicitly relying on lexicographic ordering. I don't think that I think that defining lexicographic behaviour for But as a user, I would be surprised to find the below code fail: let first = { foo: 1, bar: 2 };
let second = { foo: 2, bar: 1};
assert!(first < second); |
This comment has been minimized.
This comment has been minimized.
|
@clarcharr I've added an unresolved question to be resolved prior to merging (if we do that...) about whether
I would have loved if we had used
Sure; I entirely agree with that sentiment; it is by far the biggest drawback.
I noted somewhere in the RFC that with the combination of const generics and variadic generics, you may be able to extend this to structural records as well. For example (please note that this is 100% a sketch), using a temporary syntax due to Yato: impl
<*(T: serde::Serialize, const F: meta::Field)>
// quantify a list of pairs with:
// - a) type variables T all bound by `serde::Serialize`,
// - b) a const generic variables F standing in for the field of type meta::Field
// where `meta` is the crate we reserved and `Field` is a compile time
// reflection / polymorphism mechanism for fields.
serde::Serialize
for
{ *(F: T) }
// The structural record type; (Yes, you need to extend the type grammar *somehow*..)
{
// logic...
}This idea is inspired by Glasgow Haskell's |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Feb 26, 2019
|
We noticed a handy trick for replacing structural records: We permit types declared
In essence, We might support more polymorphism this way than with structural records, but obviously structural records simplify everything sometimes. We've some complex type parameters in |
This comment has been minimized.
This comment has been minimized.
Stargateur
commented
Feb 26, 2019
•
@iopq And what if one library use |
This comment has been minimized.
This comment has been minimized.
ssokolow
commented
Feb 26, 2019
•
From that "optimize for interoperation without conversion" perspective, the best solution is a Of course, it's also always possible that you'll find There's no magic bullet. The best you can get is to have a standard crate which just defines types like |
This comment has been minimized.
This comment has been minimized.
jswrenn
commented
Feb 26, 2019
|
@Stargateur totally valid concerns, and for those reasons, I don't think we'll see ad hoc record types appear in the public API of many crates. Ad hoc records aren't really a replacement for named records; they're more closely a substitute for Rust's other ad hoc type: tuples. We generally use tuples for the one-off packings of data that you need when you're doing transformations over that data; e.g., iterator chains, or internal helper functions. Tuples are the right tool to use when giving a name to a type is actually more cognitive load than just bunching the data all together (and there is some cognitive load to naming a type: you need to pick a name, pick a place to define it, manage imports, and look back at the definition). The big problem with tuples is they aren't really self-documenting: you need to always remember what order the components are in. If the components have different types, you'll be saved from mistakes by a compiler error. If they don't, you're hosed. I think of named records as basically just being tuples where the fields aren't named And that's the role I need them for. In my work I do a lot of transformations over deep, tree-structured data for analysis purposes. The input types are all named structures (thanks serde!) and the output types are all named structures too. What happens in the middle is a complete mess of tuples, though. I do dozens of one-off transformations of data. The cognitive load (and code bloat) of turning all of those tuples into name structures would be totally unmanagable. But: trying to decipher iterator chains that use tuples also sucks! Ad hoc records are going to really improve my quality of life in programming Rust. |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Feb 26, 2019
|
I think transformations over deeply structured data sounds like lenses, but structural records would often be simpler. |
This comment has been minimized.
This comment has been minimized.
|
But Color has a canonical ordering A 2d rectangle doesn't, but Not having to declare it somewhere seems like a win |
This comment has been minimized.
This comment has been minimized.
ssokolow
commented
Feb 28, 2019
•
|
Actually, color does not have a canonical ordering. For historical reasons, you run into BGR as a legacy detail that has persisted in various Windows file formats and APIs and it also crops up when the abstraction leaks on a little-endian system. For example, I got tripped up when using PyOpenCV because its use of 32-bit integers as an internal representation results in BGR(A) content on a little-endian processor and (A)RGB on a big-endian processor. (I wound up very confused about why my images were coming out wrong until I finally managed to determine that it was my assumptions about the mapping between color components and indices... the exact kind of bug named struct fields prevent.) Likewise, Google's design document for Skia has a section named "The epic battle of RGBA and BGRA" which talks about having to do conversion between RGB(A) file formats and BGRA memory representations. (Another situation where bugs are likely to creep in when using numeric indices.) As for 2D rectangles, I've always seen them in one of two representations:
In both cases, taking after the |
This comment has been minimized.
This comment has been minimized.
Note that this is the same thing that C# hit, which I mentioned in #2584 (comment) -- it doesn't necessarily need structural types, just anonymous ones often work great. |
This comment has been minimized.
This comment has been minimized.
jhpratt
commented
Apr 29, 2019
•
|
I don't believe anyone else has mentioned this, but going back to July 2012, Rust 0.3 had support for what is described to be "structural records", and the syntax is similar. (docs). I'm not sure if that syntax just morphed into the current |
This comment has been minimized.
This comment has been minimized.
|
The old structural records were intentionally removed. See https://internals.rust-lang.org/t/pre-rfc-unnamed-struct-types/3872/31?u=ixrec IIUC, the current proposal and its context are different enough (if only because we already have nominal structs) that the reasoning in that post does not directly apply here. |
This comment has been minimized.
This comment has been minimized.
LukeSkyw
commented
Jul 24, 2019
|
I am not an expert in Rust and don't pretend to understand everything being said in this thread but I am very interested by this proposal and would like to see it adopted. So I am trying to sum up the outstanding issues that has popped up:
Is this RFC stalled because there is no interest to pursue it further by rust language team? Then shouldn't it be closed? |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Jul 24, 2019
|
There are much higher proprieties than this syntactic sugar, and all syntax incurs some cognitive cost so assessing the cost vs benefits remain hard, even if you can solve the various outstanding issues. I proposed in #2584 (comment) that we merely allow There are even more limited options like attaching items doc comments to other items, but maybe too limited for serious adoption. |
This comment has been minimized.
This comment has been minimized.
|
It's not syntactic sugar, it actually creates a new kind of types. It lets you easily group things when passing stuff between functions of different crates. Right now you either need a struct in std, or to import a common crate. |
This comment has been minimized.
This comment has been minimized.
jswrenn
commented
Jul 24, 2019
•
|
@burdges, I'm impressed with how many use-cases are covered by your proposal (in large part because of @scottmcm's closely related comment #2584 (comment)). I believe this is a viable alternative to structural records for the purposes of code generation (which is one of my motivations for wanting structural records #2584 (comment)). I would really love to see this feature explored in an RFC. However:
When I need an ad-hoc structure type in an iterator chain, I often choose to use a tuple, because a full-blown structure definition would decrease the readability of my code. In these situations, I would like to use a structural record, but must use a tuple instead. This decision always leaves me deeply unsatisfied, because tuples are almost always bad for readability. A tuple is actually just a special-case of a structural record, in which the field names are consecutive integers. (This correspondence is already reflected in Rust.) Let's imagine an alternate reality where Rust had ad-hoc records, but no parenthetical tuple initialization syntax for it. How often would you choose to use numbers as field names? For me, it would be about as often as I use variable names like |
This comment has been minimized.
This comment has been minimized.
LukeSkyw
commented
Jul 24, 2019
•
|
For what it's worth, this pattern is actually familiar to Javascript/Typescript developers (which numbers are growing I believe): function foo(): { bar: Number, baz: Number } {
const bar = // ... compute some value
const baz = // ... compute some value
return { bar, baz }
}
const { bar, baz } = foo();When used with de-structuring, it is a very convenient and expressive way of reducing cognitive load when returning data that do not necessarily fit together into a predefined struct. EDIT: By the way, the object literal shorthand syntax could be nice to have too, but let's not push our luck ;) |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Jul 24, 2019
|
Interestingly, you could likey define a You also cannot define anonymous types, ala closures, using |
This comment has been minimized.
This comment has been minimized.
Stargateur
commented
Jul 24, 2019
•
I already prove this is wrong. The same will happen with Strutural records. The only case is when you are lucky to use the same name if you not you will also need a breaking change like today. |
This comment has been minimized.
This comment has been minimized.
jswrenn
commented
Jul 30, 2019
|
I think this RFC's take on structural records is interesting in its own right, but I'm also excited about the future design possibilities it opens up. Row PolymorphismAt the bottom of #2584 (comment), I wrote that the potential future addition of row polymorphism to structural records had exciting ergonomics implications. E.g.: // Transform a stream of things with euclidian coordinates into a stream of
// things polar coordinates.
//
// `Ρ` denotes "some collection of additional fields and types"
//
// this type signature conveys that the _only_ thing altered between the
// input and output types is the `pos` field.
fn convert<Ρ>(input: impl IntoIterator<Item={pos: EuclidianCoordinate, ..Ρ}>)
-> impl Iterator<Item={pos: PolarCoordinate, ..Ρ}>
{
input
.into_iter()
.map(| {pos, ..ρ} : {pos: EuclidianCoordinate, ..Ρ} |
{pos: pos.into_polar_coordinate(), ..ρ})
}Given this... Generic Trait Impls over Ad-Hoc Types...I think there's a natural path towards generic trait impls over ad-hoc types like tuples. To borrow a bit of syntax from // `Ρ` denotes "some collection of additional fields and types"
// `_ : T` denotes a field of any name
impl<T, Ρ> PartialEq for {_ : T, Ρ..}
where
T: PartialEq,
P: PartialEq,
{
// `rhs_first@_ : T` binds the value of `T` to `rhs_first`
fn eq(&self, other: &{rhs_first@_ : T, rhs_rest@Ρ..}) -> bool {
let {lhs_first@_ : T, lhs_rest@Ρ..} = self;
(lhs_first == rhs_first) && (lhs_rest == rhs_rest)
}
}This would cover some use-cases of #2702 and avoid the layout implications. |
This comment has been minimized.
This comment has been minimized.
zzyyxxww
commented
Oct 12, 2019
•
|
I can understand the opposition to this rfc based on implementation/technical difficulties. Excessive types and type conversion code have been a big problem for a long time for me when using statically typed langs. For example, let's say you have a Usually what happens is that some functions access some of these fields, but not all of them. It basically turns into a wild west of types and names. You could say "just pass the entire struct", but sometimes you have similar structs like And you can expect this boilerplate to happen when trying to combine funcs from different crates. You could say "just pass a tuple or separate arguments". But tuples or separate arguments are just awful for our example because of having the same type, so if you try to "change" the tuple (as in, you expect different values or different orders) but keep the same length, you won't get any compile error yet every call is now passing the incorrect value. So, the best choice is to stick to structs, but it results in annoying and needless amounts of boilerplate code. The concept of ad-hoc types is very valuable. That's why there's this relief when going from a statically typed language to for example javascript, because of the realization that you don't need to write superfluous types or superfluous type conversion code anymore (with the expense of total uncertainty, ofc). But I hope rust acquires this feature at some point. Regarding comments from other users: @ssokolow says "use common types from the same crate". That is too ideal and doesn't happen in practice. People will declare whatever types they want to and I think that's fine. @Stargateur says that if two structs have equivalent but different names, then you will get a breaking change. Yes, but the same can happen with tuples if they are expected in different orders. |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Oct 12, 2019
|
I do like structural records, but they interact closely with almost every major type system extension being considered. I've posted 5-10 comments exploring such interactions inside different threads here. I'd suggest that delegation and trait fields be sorted out first in particular, which sounds like another couple years. And varadic tuples should wait a couple years for similar reasons All your examples sound like dynamic issues not really resolvable at compile time. As a rule, anything like
In more constrained cases, you should often provided a statically typed exterior that enforces any important constraints, but use appropriate "dynamism" internally, both for developer sanity and to represent the full protocol flow. If your code has extremely hairy types then you could always go fully dynamic by using I've never done that, but I once used a Just fyi, there are also formal verification techniques like refinement types that push more dynamic information into the type level. At least refinement types do provide remarkable improvements in syntax, like the whole |
This comment has been minimized.
This comment has been minimized.
|
First, thank you so much for this RFC. I didn’t know it existed until today. It’s always a pleasure to read such well-written RFCs. Congrats!
I disagree with this. As stated above, I think that feature is providing a feature that is not necessary, but about ergonomics. It doesn’t bring anything new (you could be using a tuple, or a one-shot struct). It’s just comfort, it won’t make your program more correct. I don’t quite know how to feel about it, to be honest. Currently, my mind is just picturing them as “unordered tuples with records”. I personally think I have no use for them — types are everything to me: the stronger the better. I sometimes need ordered unnamed types (i.e. tuples), but unordered tuples… I don’t see any useful use case for them. About positional arguments in functions, I sometimes wonder whether I would be for them or not. But I agree this RFC shouldn’t be considered regarding positional arguments. |
This comment has been minimized.
This comment has been minimized.
phlopsi
commented
Dec 18, 2019
•
|
This is awesome. I can't wait to try this out. For someone like me, who values code readability highly, this is the perfect feature. It comes in handy whenever I have to temporarily store partial structs in some collection and I don't want to create a type that is only used in 1 to 2 functions and e.g. call it |
This comment has been minimized.
This comment has been minimized.
matthew-mcallister
commented
Jan 23, 2020
•
|
In addition to containers, such a feature would combat a common breed of boilerplate caused by mutexes: struct ObjectPool {
memory_limit: usize,
objects: Mutex<{
total_objects: usize,
free: Vec<Object>,
}>,
}Typically you either mutex the whole object or define a poorly named struct like |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Jan 24, 2020
|
In #2584 (comment) @jswrenn writes:
I think structural records do not address the has-field requirements on types anyways. If I write, We should address ad-hoc return types with types declared We could separately address has-field requirements with a crate that provides a trait like
or more likely
either of which get implemented by a procmacro like
I think this |
This comment has been minimized.
This comment has been minimized.
rodrimati1992
commented
Jan 24, 2020
•
|
In pub trait GetField<const NAME:&'static str> {
type Ty:?Sized;
fn get_field_(&self) -> &Self::Ty;
fn get_field_mut_(&mut self) -> &mut Self::Ty;
fn into_field_(self) -> Self::Ty
where Self:Sized,Self::Ty:Sized;
}Edit:I hadn't noticed that the RFC also proposes a very similar trait as a future possibility. But with a trait hierarchy for shared/mutable/by value access,a With this design it's possible to bound the type of a field with pub fn hello(iter:impl IntoIterator<Item=impl GetField<"name",Ty=impl Debug>>){
for item in iter {
println!("{:?}",GetField::<"name">::get_field_(&item));
}
} |
This comment has been minimized.
This comment has been minimized.
|
Anonymous structs with derived or manual implementations are (already) possible, albeit with less-concise syntax. For example, one could have the following via proc-macro: let x = make_struct! {
#[derive(Clone, Copy, Debug)]
struct {
x: u32 = 52,
y = calc_y(),
_ = anon_field_value(),
}
impl MyTrait { // implicitly for the above struct
...
}
};Not very concise, but it still beats naming all types and fields and using separate type definition and value instantiation. (And it can be done with a proc-macro today via generics, although it would work better with Permit _ in type aliases. My Similarly, if no implementations are wanted, a simpler macro could be used: let y = make_record! { a = 30.0f64, b = true };This does already meet some of the motivation for this RFC, although of course a language feature has certain advantages. |
This comment has been minimized.
This comment has been minimized.
burdges
commented
Jan 24, 2020
•
|
Oh nice @rodrimati1992 I think your trait makes more sense than either of mine. I think the refraction crate has some similar ideas coming form lenses. And the generic-field-projection crate. I think even if const generics worked then you'd currently run into issues with the lack of lazy normalization like in #2289 but after it gets fixed then more should work. |
Centril commentedNov 2, 2018
Introduce structural records of the form
{ foo: 1u8, bar: true }of type{ foo: u8, bar: bool }into the language. Another way to understand these sorts of objects is to think of them as "tuples with named fields", "unnamed structs", or "anonymous structs".To @kennytm, @alexreg, @Nemo157, and @tinaun for reviewing the draft version of this RFC.
To @varkor and @pnkfelix for good and helpful discussions.