copying this here so discussing it is less cluttered:
a quick idea: instead of desugaring to tuples, how about desugaring to an instance of a named struct in std:
// in std::somewhere pub struct AnonymousStruct<Fields: std::marker::Tuple, const NAMES: &'static [&'static str]>(pub Fields); impl<Fields: std::marker::Tuple, const NAMES: &'static [&'static str]> AnonymousStruct<Fields, NAMES> { /// Self::get_field_index("abc") = Ok(5) means self.abc desugars to self.0.5 pub const fn get_field_index(name: &str) -> Result<usize, ()> { const { assert!(NAMES.is_sorted(), "NAMES must be sorted"); }; NAMES.binary_search(&name).map_err(|_| ()) } }
pseudo-code:
fn desugar_anonymous_struct_type(fields: Vec<(Member, Type)>) -> Result<Type> { // sorts by field name let mut fields: BTreeMap<Member, Type> = fields.into_iter().collect(); let mut tuple_fields: Punctuated<Type, _> = (0..).map_while(|i| fields.remove(&Member::Unnamed(i.into()))).collect(); if fields.is_empty() { // actually a tuple, since all fields are named using successive integers return Ok(TupleType { paren: Paren::default(), elems: tuple_fields }.into()); } if !tuple_fields.is_empty() { bail!("all fields must be named"); } let mut keys = vec![]; let mut types = vec![]; for (k, v) in fields { let Member::Named(k) = k else { bail!("all fields must be named"); }; keys.push(LitStr::new(&k.to_string(), k.span())); types.push(v); } Ok(parse_quote!(::std::somewhere::AnonymousStruct<(#(#types,)*), { &[#(#keys),*] }>)) }
I would expect the code for the field access operator
.
to be structured such that a special case can be easily added for a lhs ofAnonymousStruct
since it needs to resolve the lhs type anyway in order to resolve existing types' field names.
desugaring to a named struct in
std
instantiated with a tuple of the sorted field types and a const generic slice of sorted field names for the anonymous struct neatly solves nearly all the implementation and how-to-impl-traits-for-all-anonymous-structs issues afaict.