Emit trait default bodies as generic procs
rustnim refused any trait with a default body: trait resolution is not
modelled, so there was no impl to emit the body into, and dropping it would
have removed code the implementors inherit. That refusal held for the whole
project until now.
It turns out not to need trait resolution. A default body is code that applies
to every implementor, which is what a Nim generic proc is -- and trait methods
were already named `rs{Trait}_{method}` and overloaded on the receiver, so the
default emitted with `Self` as a type parameter is the fallback and an `impl`
that overrides it is a more specific overload. Nim prefers the specific one.
That is Rust's rule, arrived at by Nim's.
Four things had to come with it:
* A call to an inherited method resolves through the traits the receiver's
type implements, *before* the arguments are lowered -- the trait's
signature is what says their types, and a closure argument has no shape
without it.
* `F: FnOnce(&Self) -> R` says `F` is callable and with what signature, so a
body can call what it was handed. `Self` has to be a type while those
bounds are collected or the bound cannot be recorded at all.
* `subst` now descends into proc types. It did not, so `Self` survived into
the expectation and a closure came out annotated `proc (x: Self)`.
* A return type that is a trait parameter appearing in no argument cannot be
inferred from a call. Nim's `auto` takes it from the body -- where Rust's
monomorphisation gets it too -- and the parameter is then not a parameter
at all, so it is dropped from the generic list.
`apply` compiles, and `tests/cases/071` covers an inherited default, an
overridden one, a default calling another default, and one that invokes a
closure it was passed.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>7e4c463 parent: 211aebe modified
src/lower.rs +205 -15 | @@ -381,6 +381,14 @@ pub struct Lowerer { | ||
| 381 | 381 | /// the closure must have -- which is the only thing that types a closure |
| 382 | 382 | /// passed to a generic parameter. |
| 383 | 383 | fn_blankets: HashMap<String, (Vec<String>, Vec<Nim>, Nim)>, |
| 384 | + /// Generic parameters that are callable: `F: FnOnce(A) -> B`. | |
| 385 | + fn_bounds: HashMap<String, Nim>, | |
| 386 | + /// Which traits each type implements, so a method it does not declare | |
| 387 | + /// can still be found on a trait that gives it a default body. | |
| 388 | + type_traits: HashMap<String, Vec<(String, Vec<Nim>)>>, | |
| 389 | + /// Return type to use instead of the signature's, for a default body | |
| 390 | + /// whose return type Nim cannot infer. | |
| 391 | + ret_override: Option<Nim>, | |
| 384 | 392 | /// Procs that cannot be translated, by Nim name, with the reason. |
| 385 | 393 | /// |
| 386 | 394 | /// Not an error on its own. A dependency graph carries far more code than |
| @@ -504,6 +512,9 @@ impl Lowerer { | ||
| 504 | 512 | opaque_why: None, |
| 505 | 513 | trait_methods: HashMap::new(), |
| 506 | 514 | fn_blankets: HashMap::new(), |
| 515 | + fn_bounds: HashMap::new(), | |
| 516 | + type_traits: HashMap::new(), | |
| 517 | + ret_override: None, | |
| 507 | 518 | untranslatable: HashMap::new(), |
| 508 | 519 | enum_module: HashMap::new(), |
| 509 | 520 | report: false, |
| @@ -1003,9 +1014,16 @@ impl Lowerer { | ||
| 1003 | 1014 | // so what is needed here is the *signature*, not a body. |
| 1004 | 1015 | let tname = t.ident.to_string(); |
| 1005 | 1016 | let tgen = Self::generics_of(&t.generics); |
| 1006 | - let outer = std::mem::replace(&mut self.impl_generics, tgen.clone()); | |
| 1017 | + let mut g = vec!["Self".to_string()]; | |
| 1018 | + g.extend(tgen.clone()); | |
| 1019 | + let outer = std::mem::replace(&mut self.impl_generics, g); | |
| 1020 | + // `Self` has to be a type here, or a bound written in terms of | |
| 1021 | + // it -- `F: FnOnce(&Self) -> R` -- cannot be recorded at all. | |
| 1022 | + let outer_self = | |
| 1023 | + self.self_ty.replace(Nim::Named("Self".into(), Vec::new())); | |
| 1007 | 1024 | for it in &t.items { |
| 1008 | 1025 | if let syn::TraitItem::Fn(f) = it { |
| 1026 | + self.note_bounds(&f.sig.generics); | |
| 1009 | 1027 | if let Ok((mut params, ret)) = self.signature(&f.sig) { |
| 1010 | 1028 | if takes_self(&f.sig) { |
| 1011 | 1029 | params.insert(0, Nim::Named("Self".into(), Vec::new())); |
| @@ -1018,6 +1036,7 @@ impl Lowerer { | ||
| 1018 | 1036 | } |
| 1019 | 1037 | } |
| 1020 | 1038 | self.impl_generics = outer; |
| 1039 | + self.self_ty = outer_self; | |
| 1021 | 1040 | } |
| 1022 | 1041 | Item::Static(st) => { |
| 1023 | 1042 | // The name and type, so a body reached before the emission |
| @@ -1214,6 +1233,25 @@ impl Lowerer { | ||
| 1214 | 1233 | self.note_const_generics(&im.generics)?; |
| 1215 | 1234 | self.note_bounds(&im.generics); |
| 1216 | 1235 | self.note_fn_blanket(im); |
| 1236 | + if let (Some((path, _)), Ok(t)) = (&im.trait_, self.map_ty(&im.self_ty)) { | |
| 1237 | + if let Some(seg) = path.segments.last() { | |
| 1238 | + let targs: Vec<Nim> = match &seg.arguments { | |
| 1239 | + syn::PathArguments::AngleBracketed(a) => a | |
| 1240 | + .args | |
| 1241 | + .iter() | |
| 1242 | + .filter_map(|g| match g { | |
| 1243 | + GenericArgument::Type(x) => self.map_ty(x).ok(), | |
| 1244 | + _ => None, | |
| 1245 | + }) | |
| 1246 | + .collect(), | |
| 1247 | + _ => Vec::new(), | |
| 1248 | + }; | |
| 1249 | + self.type_traits | |
| 1250 | + .entry(type_name(&t)) | |
| 1251 | + .or_default() | |
| 1252 | + .push((seg.ident.to_string(), targs)); | |
| 1253 | + } | |
| 1254 | + } | |
| 1217 | 1255 | // Associated consts are registered here for the same reason anything |
| 1218 | 1256 | // else is: an array length elsewhere may name one, and that name has |
| 1219 | 1257 | // to resolve before any body is lowered. `assoc_const` records them |
| @@ -1851,6 +1889,23 @@ impl Lowerer { | ||
| 1851 | 1889 | for b in bounds { |
| 1852 | 1890 | let syn::TypeParamBound::Trait(tb) = b else { continue }; |
| 1853 | 1891 | let Some(seg) = tb.path.segments.last() else { continue }; |
| 1892 | + // `F: FnOnce(A) -> B` says `F` *is* a proc of that shape, | |
| 1893 | + // which is what lets the body call it. | |
| 1894 | + if matches!(seg.ident.to_string().as_str(), "Fn" | "FnMut" | "FnOnce") { | |
| 1895 | + if let syn::PathArguments::Parenthesized(pa) = &seg.arguments { | |
| 1896 | + let args: Result<Vec<Nim>, String> = | |
| 1897 | + pa.inputs.iter().map(|x| me.map_ty(&x.ty)).collect(); | |
| 1898 | + let ret = match &pa.output { | |
| 1899 | + ReturnType::Default => Ok(Nim::Unit), | |
| 1900 | + ReturnType::Type(_, t) => me.map_ty(t), | |
| 1901 | + }; | |
| 1902 | + if let (Ok(a), Ok(r)) = (args, ret) { | |
| 1903 | + me.fn_bounds | |
| 1904 | + .insert(name.clone(), Nim::Proc(a, Box::new(r))); | |
| 1905 | + } | |
| 1906 | + } | |
| 1907 | + continue; | |
| 1908 | + } | |
| 1854 | 1909 | let args: Vec<Nim> = match &seg.arguments { |
| 1855 | 1910 | syn::PathArguments::AngleBracketed(a) => a |
| 1856 | 1911 | .args |
| @@ -2010,6 +2065,16 @@ impl Lowerer { | ||
| 2010 | 2065 | Nim::Array(n, e) => Nim::Array(n.clone(), Box::new(Self::subst(e, m))), |
| 2011 | 2066 | Nim::Var(e) => Nim::Var(Box::new(Self::subst(e, m))), |
| 2012 | 2067 | Nim::Tuple(ts) => Nim::Tuple(ts.iter().map(|x| Self::subst(x, m)).collect()), |
| 2068 | + // A proc type carries parameters too: `F: FnOnce(&Self) -> R` is | |
| 2069 | + // useless as an expectation if `Self` survives into it. | |
| 2070 | + Nim::Proc(a, r) => Nim::Proc( | |
| 2071 | + a.iter().map(|x| Self::subst(x, m)).collect(), | |
| 2072 | + Box::new(Self::subst(r, m)), | |
| 2073 | + ), | |
| 2074 | + Nim::Ptr(e) => Nim::Ptr(Box::new(Self::subst(e, m))), | |
| 2075 | + Nim::ConstPtr(e) => Nim::ConstPtr(Box::new(Self::subst(e, m))), | |
| 2076 | + Nim::Iter(e) => Nim::Iter(Box::new(Self::subst(e, m))), | |
| 2077 | + Nim::Future(e) => Nim::Future(Box::new(Self::subst(e, m))), | |
| 2013 | 2078 | other => other.clone(), |
| 2014 | 2079 | } |
| 2015 | 2080 | } |
| @@ -2838,22 +2903,56 @@ impl Lowerer { | ||
| 2838 | 2903 | Item::Macro(m) if path_name(&m.mac.path) == "macro_rules" => Ok(()), |
| 2839 | 2904 | Item::Type(_) => Ok(()), // expanded at every use site |
| 2840 | 2905 | Item::Trait(t) => { |
| 2841 | - // We do not model trait resolution, so a declaration generates | |
| 2842 | - // nothing and a use that needed it is rejected where it | |
| 2843 | - // appears. A *default body*, though, is code: dropping it | |
| 2844 | - // would silently remove a method the impls inherit. | |
| 2906 | + // A trait declaration generates nothing by itself. A *default | |
| 2907 | + // body* is code, though, and it is code that applies to every | |
| 2908 | + // implementor -- which is what a Nim generic proc is. | |
| 2909 | + // | |
| 2910 | + // Trait methods are already named `rs{Trait}_{method}` and | |
| 2911 | + // overloaded on the receiver, so a default body emitted with | |
| 2912 | + // `Self` as a type parameter is the fallback, and an `impl` | |
| 2913 | + // that overrides it is a more specific overload that Nim | |
| 2914 | + // prefers. That is Rust's rule, arrived at by Nim's. | |
| 2915 | + let tname = t.ident.to_string(); | |
| 2916 | + let tgen = Self::generics_of(&t.generics); | |
| 2845 | 2917 | for it in &t.items { |
| 2846 | - if let syn::TraitItem::Fn(f) = it { | |
| 2847 | - if f.default.is_some() { | |
| 2848 | - return Err(format!( | |
| 2849 | - "`trait {}` gives `{}` a default body; trait \ | |
| 2850 | - resolution is not modelled, so that body has no \ | |
| 2851 | - impl to be emitted into and dropping it would \ | |
| 2852 | - remove code", | |
| 2853 | - t.ident, f.sig.ident | |
| 2854 | - )); | |
| 2918 | + let syn::TraitItem::Fn(f) = it else { continue }; | |
| 2919 | + let Some(body) = &f.default else { continue }; | |
| 2920 | + let outer_g = std::mem::replace(&mut self.impl_generics, { | |
| 2921 | + let mut g = vec!["Self".to_string()]; | |
| 2922 | + g.extend(tgen.clone()); | |
| 2923 | + g | |
| 2924 | + }); | |
| 2925 | + let outer_self = | |
| 2926 | + self.self_ty.replace(Nim::Named("Self".into(), Vec::new())); | |
| 2927 | + // The method's own bounds: `fn apply<F: FnOnce(Self) -> Res>` | |
| 2928 | + // is what says `f` can be called. | |
| 2929 | + self.note_bounds(&f.sig.generics); | |
| 2930 | + let nim = trait_method_name( | |
| 2931 | + &tname, | |
| 2932 | + &tname, | |
| 2933 | + &f.sig.ident.to_string(), | |
| 2934 | + takes_self(&f.sig), | |
| 2935 | + ); | |
| 2936 | + let recv = takes_self(&f.sig) | |
| 2937 | + .then(|| Nim::Named("Self".into(), Vec::new())); | |
| 2938 | + // A return type that is a trait parameter appearing | |
| 2939 | + // nowhere in the arguments cannot be inferred from a call. | |
| 2940 | + // Nim's `auto` takes it from the body, which is where | |
| 2941 | + // Rust's monomorphisation gets it too. | |
| 2942 | + if let Ok((ptys, rt)) = self.signature(&f.sig) { | |
| 2943 | + if let Nim::Named(g, a) = &rt { | |
| 2944 | + if a.is_empty() | |
| 2945 | + && g != "Self" | |
| 2946 | + && !ptys.iter().any(|p| Self::mentions_any(p, &[g.clone()])) | |
| 2947 | + { | |
| 2948 | + self.ret_override = Some(Nim::Prim("auto".into())); | |
| 2949 | + } | |
| 2855 | 2950 | } |
| 2856 | 2951 | } |
| 2952 | + let r = self.func_named(&nim, &f.sig, body, recv); | |
| 2953 | + self.self_ty = outer_self; | |
| 2954 | + self.impl_generics = outer_g; | |
| 2955 | + r?; | |
| 2857 | 2956 | } |
| 2858 | 2957 | Ok(()) |
| 2859 | 2958 | } |
| @@ -3651,6 +3750,9 @@ impl Lowerer { | ||
| 3651 | 3750 | self.opaque_hint = self.opaque_ret.get(&opaque_key(sig, body)).cloned(); |
| 3652 | 3751 | let (ptys, ret) = self.signature(sig)?; |
| 3653 | 3752 | self.opaque_hint = None; |
| 3753 | + let trim = self.ret_override.is_some(); | |
| 3754 | + let recv_ty = recv.clone(); | |
| 3755 | + let ret = self.ret_override.take().unwrap_or(ret); | |
| 3654 | 3756 | |
| 3655 | 3757 | self.push_scope(); |
| 3656 | 3758 | let mut rendered: Vec<String> = Vec::new(); |
| @@ -3705,6 +3807,18 @@ impl Lowerer { | ||
| 3705 | 3807 | gparams.push(g); |
| 3706 | 3808 | } |
| 3707 | 3809 | } |
| 3810 | + // A parameter that appears in no argument cannot be bound by a call. | |
| 3811 | + // With an `auto` return there is nothing else to bind it either, so it | |
| 3812 | + // is not a parameter at all -- it was only ever the return type's name. | |
| 3813 | + if trim { | |
| 3814 | + // The receiver counts: `self: Self` is what binds `Self`, and it | |
| 3815 | + // is not one of `ptys`. | |
| 3816 | + let mut bound: Vec<Nim> = ptys.clone(); | |
| 3817 | + if let Some(r) = &recv_ty { | |
| 3818 | + bound.push(r.clone()); | |
| 3819 | + } | |
| 3820 | + gparams.retain(|g| bound.iter().any(|p| Self::mentions_any(p, &[g.clone()]))); | |
| 3821 | + } | |
| 3708 | 3822 | let gens = self.gen_list(&gparams); |
| 3709 | 3823 | let outer_fg = std::mem::replace(&mut self.fn_generics, gparams.clone()); |
| 3710 | 3824 | let head = if ret == Nim::Unit { |
| @@ -6938,6 +7052,68 @@ impl Lowerer { | ||
| 6938 | 7052 | format!("(block:\n{}\n{pad}{})", copies.join("\n"), proc_code) |
| 6939 | 7053 | } |
| 6940 | 7054 | |
| 7055 | + /// A method the receiver's type does not declare, but a trait it | |
| 7056 | + /// implements does -- an inherited default body. | |
| 7057 | + /// | |
| 7058 | + /// Those are emitted as generic procs under the trait's name, so the call | |
| 7059 | + /// is the same one an overriding impl would answer; Nim picks the more | |
| 7060 | + /// specific overload where there is one, which is Rust's rule. | |
| 7061 | + fn inherited_default( | |
| 7062 | + &mut self, | |
| 7063 | + m: &syn::ExprMethodCall, | |
| 7064 | + name: &str, | |
| 7065 | + recv: &Val, | |
| 7066 | + ) -> Result<Option<Val>, String> { | |
| 7067 | + let Some(rt) = recv.ty.clone() else { return Ok(None) }; | |
| 7068 | + let rn = type_name(&rt.clone().unvar()); | |
| 7069 | + if self.fn_generics.iter().any(|g| *g == rn) { | |
| 7070 | + return Ok(None); | |
| 7071 | + } | |
| 7072 | + if self.methods.contains_key(&(rn.clone(), name.to_string())) { | |
| 7073 | + return Ok(None); | |
| 7074 | + } | |
| 7075 | + let trs = self.type_traits.get(&rn).cloned().unwrap_or_default(); | |
| 7076 | + let Some((tr, targs)) = trs | |
| 7077 | + .iter() | |
| 7078 | + .find(|(tr, _)| self.trait_methods.contains_key(&(tr.clone(), name.to_string()))) | |
| 7079 | + else { | |
| 7080 | + return Ok(None); | |
| 7081 | + }; | |
| 7082 | + let sig = self.trait_methods[&(tr.clone(), name.to_string())].clone(); | |
| 7083 | + // `Self` is the receiver's type here, and the trait's own parameters | |
| 7084 | + // are whatever the `impl` supplied for them. | |
| 7085 | + let mut sm: HashMap<String, Nim> = HashMap::new(); | |
| 7086 | + sm.insert("Self".into(), rt.clone().unvar()); | |
| 7087 | + for (p, a) in sig.generics.iter().zip(targs.iter()) { | |
| 7088 | + sm.insert(p.clone(), a.clone()); | |
| 7089 | + } | |
| 7090 | + let mut codes = vec![recv.code.clone()]; | |
| 7091 | + for (i, a) in m.args.iter().enumerate() { | |
| 7092 | + let raw = sig.params.get(i + 1).cloned(); | |
| 7093 | + // A parameter that is a callable generic says what shape a | |
| 7094 | + // closure written here has. | |
| 7095 | + let want = match &raw { | |
| 7096 | + Some(Nim::Named(g, _)) if self.fn_bounds.contains_key(g) => { | |
| 7097 | + Some(Self::subst(&self.fn_bounds[g].clone(), &sm)) | |
| 7098 | + } | |
| 7099 | + Some(t) if !Self::mentions_any(t, &sig.generics) && type_name(t) != "Self" => { | |
| 7100 | + Some(t.clone()) | |
| 7101 | + } | |
| 7102 | + _ => None, | |
| 7103 | + }; | |
| 7104 | + codes.push(self.expr_at(a, want.as_ref())?.code); | |
| 7105 | + } | |
| 7106 | + let ret = Self::subst(&sig.ret, &sm); | |
| 7107 | + Ok(Some(Val::new( | |
| 7108 | + format!( | |
| 7109 | + "{}({})", | |
| 7110 | + trait_method_name(&rn, tr, name, true), | |
| 7111 | + codes.join(", ") | |
| 7112 | + ), | |
| 7113 | + Some(ret), | |
| 7114 | + ))) | |
| 7115 | + } | |
| 7116 | + | |
| 6941 | 7117 | /// `|x| x + 1` -> a Nim anonymous proc. |
| 6942 | 7118 | /// |
| 6943 | 7119 | /// Nim's closures capture by reference, as Rust's non-`move` closures do. |
| @@ -7768,7 +7944,13 @@ impl Lowerer { | ||
| 7768 | 7944 | // conversion, e.g. `String::from(..)`; handled by the method path. |
| 7769 | 7945 | // Calling a proc-typed local, which is how an `impl Fn(..)` parameter |
| 7770 | 7946 | // is invoked. |
| 7771 | - if let Some(Nim::Proc(ptys, ret)) = self.lookup(&name) { | |
| 7947 | + // A local whose type is a generic parameter bound by `Fn`: the bound | |
| 7948 | + // says it is callable, and with what signature. | |
| 7949 | + let callee_ty = match self.lookup(&name) { | |
| 7950 | + Some(Nim::Named(g, _)) => self.fn_bounds.get(&g).cloned(), | |
| 7951 | + other => other, | |
| 7952 | + }; | |
| 7953 | + if let Some(Nim::Proc(ptys, ret)) = callee_ty { | |
| 7772 | 7954 | // Re-lowered against the proc's own parameter types: a literal |
| 7773 | 7955 | // argument takes its width from them, and nothing earlier in this |
| 7774 | 7956 | // function knew them. |
| @@ -8073,6 +8255,14 @@ impl Lowerer { | ||
| 8073 | 8255 | ("push", Some(Nim::Seq(t))) | ("push", Some(Nim::OpenArray(t))) => Some((**t).clone()), |
| 8074 | 8256 | (_, t) => t.clone(), |
| 8075 | 8257 | }; |
| 8258 | + // A method the receiver's type does not declare, but a trait it | |
| 8259 | + // implements does -- an inherited default body. Resolved before the | |
| 8260 | + // arguments are lowered, because the trait's signature is what says | |
| 8261 | + // their types: a closure argument has no shape without it. | |
| 8262 | + if let Some(v) = self.inherited_default(m, &name, &recv)? { | |
| 8263 | + return Ok(v); | |
| 8264 | + } | |
| 8265 | + | |
| 8076 | 8266 | let mut args = Vec::new(); |
| 8077 | 8267 | for a in &m.args { |
| 8078 | 8268 | args.push(self.expr_at(a, arg_want.as_ref())?); |
| @@ -381,6 +381,14 @@ pub struct Lowerer { | |||
| 381 | /// the closure must have -- which is the only thing that types a closure | 381 | /// the closure must have -- which is the only thing that types a closure |
| 382 | /// passed to a generic parameter. | 382 | /// passed to a generic parameter. |
| 383 | fn_blankets: HashMap<String, (Vec<String>, Vec<Nim>, Nim)>, | 383 | fn_blankets: HashMap<String, (Vec<String>, Vec<Nim>, Nim)>, |
| 384 | + /// Generic parameters that are callable: `F: FnOnce(A) -> B`. | ||
| 385 | + fn_bounds: HashMap<String, Nim>, | ||
| 386 | + /// Which traits each type implements, so a method it does not declare | ||
| 387 | + /// can still be found on a trait that gives it a default body. | ||
| 388 | + type_traits: HashMap<String, Vec<(String, Vec<Nim>)>>, | ||
| 389 | + /// Return type to use instead of the signature's, for a default body | ||
| 390 | + /// whose return type Nim cannot infer. | ||
| 391 | + ret_override: Option<Nim>, | ||
| 384 | /// Procs that cannot be translated, by Nim name, with the reason. | 392 | /// Procs that cannot be translated, by Nim name, with the reason. |
| 385 | /// | 393 | /// |
| 386 | /// Not an error on its own. A dependency graph carries far more code than | 394 | /// Not an error on its own. A dependency graph carries far more code than |
| @@ -504,6 +512,9 @@ impl Lowerer { | |||
| 504 | opaque_why: None, | 512 | opaque_why: None, |
| 505 | trait_methods: HashMap::new(), | 513 | trait_methods: HashMap::new(), |
| 506 | fn_blankets: HashMap::new(), | 514 | fn_blankets: HashMap::new(), |
| 515 | + fn_bounds: HashMap::new(), | ||
| 516 | + type_traits: HashMap::new(), | ||
| 517 | + ret_override: None, | ||
| 507 | untranslatable: HashMap::new(), | 518 | untranslatable: HashMap::new(), |
| 508 | enum_module: HashMap::new(), | 519 | enum_module: HashMap::new(), |
| 509 | report: false, | 520 | report: false, |
| @@ -1003,9 +1014,16 @@ impl Lowerer { | |||
| 1003 | // so what is needed here is the *signature*, not a body. | 1014 | // so what is needed here is the *signature*, not a body. |
| 1004 | let tname = t.ident.to_string(); | 1015 | let tname = t.ident.to_string(); |
| 1005 | let tgen = Self::generics_of(&t.generics); | 1016 | let tgen = Self::generics_of(&t.generics); |
| 1006 | - let outer = std::mem::replace(&mut self.impl_generics, tgen.clone()); | 1017 | + let mut g = vec!["Self".to_string()]; |
| 1018 | + g.extend(tgen.clone()); | ||
| 1019 | + let outer = std::mem::replace(&mut self.impl_generics, g); | ||
| 1020 | + // `Self` has to be a type here, or a bound written in terms of | ||
| 1021 | + // it -- `F: FnOnce(&Self) -> R` -- cannot be recorded at all. | ||
| 1022 | + let outer_self = | ||
| 1023 | + self.self_ty.replace(Nim::Named("Self".into(), Vec::new())); | ||
| 1007 | for it in &t.items { | 1024 | for it in &t.items { |
| 1008 | if let syn::TraitItem::Fn(f) = it { | 1025 | if let syn::TraitItem::Fn(f) = it { |
| 1026 | + self.note_bounds(&f.sig.generics); | ||
| 1009 | if let Ok((mut params, ret)) = self.signature(&f.sig) { | 1027 | if let Ok((mut params, ret)) = self.signature(&f.sig) { |
| 1010 | if takes_self(&f.sig) { | 1028 | if takes_self(&f.sig) { |
| 1011 | params.insert(0, Nim::Named("Self".into(), Vec::new())); | 1029 | params.insert(0, Nim::Named("Self".into(), Vec::new())); |
| @@ -1018,6 +1036,7 @@ impl Lowerer { | |||
| 1018 | } | 1036 | } |
| 1019 | } | 1037 | } |
| 1020 | self.impl_generics = outer; | 1038 | self.impl_generics = outer; |
| 1039 | + self.self_ty = outer_self; | ||
| 1021 | } | 1040 | } |
| 1022 | Item::Static(st) => { | 1041 | Item::Static(st) => { |
| 1023 | // The name and type, so a body reached before the emission | 1042 | // The name and type, so a body reached before the emission |
| @@ -1214,6 +1233,25 @@ impl Lowerer { | |||
| 1214 | self.note_const_generics(&im.generics)?; | 1233 | self.note_const_generics(&im.generics)?; |
| 1215 | self.note_bounds(&im.generics); | 1234 | self.note_bounds(&im.generics); |
| 1216 | self.note_fn_blanket(im); | 1235 | self.note_fn_blanket(im); |
| 1236 | + if let (Some((path, _)), Ok(t)) = (&im.trait_, self.map_ty(&im.self_ty)) { | ||
| 1237 | + if let Some(seg) = path.segments.last() { | ||
| 1238 | + let targs: Vec<Nim> = match &seg.arguments { | ||
| 1239 | + syn::PathArguments::AngleBracketed(a) => a | ||
| 1240 | + .args | ||
| 1241 | + .iter() | ||
| 1242 | + .filter_map(|g| match g { | ||
| 1243 | + GenericArgument::Type(x) => self.map_ty(x).ok(), | ||
| 1244 | + _ => None, | ||
| 1245 | + }) | ||
| 1246 | + .collect(), | ||
| 1247 | + _ => Vec::new(), | ||
| 1248 | + }; | ||
| 1249 | + self.type_traits | ||
| 1250 | + .entry(type_name(&t)) | ||
| 1251 | + .or_default() | ||
| 1252 | + .push((seg.ident.to_string(), targs)); | ||
| 1253 | + } | ||
| 1254 | + } | ||
| 1217 | // Associated consts are registered here for the same reason anything | 1255 | // Associated consts are registered here for the same reason anything |
| 1218 | // else is: an array length elsewhere may name one, and that name has | 1256 | // else is: an array length elsewhere may name one, and that name has |
| 1219 | // to resolve before any body is lowered. `assoc_const` records them | 1257 | // to resolve before any body is lowered. `assoc_const` records them |
| @@ -1851,6 +1889,23 @@ impl Lowerer { | |||
| 1851 | for b in bounds { | 1889 | for b in bounds { |
| 1852 | let syn::TypeParamBound::Trait(tb) = b else { continue }; | 1890 | let syn::TypeParamBound::Trait(tb) = b else { continue }; |
| 1853 | let Some(seg) = tb.path.segments.last() else { continue }; | 1891 | let Some(seg) = tb.path.segments.last() else { continue }; |
| 1892 | + // `F: FnOnce(A) -> B` says `F` *is* a proc of that shape, | ||
| 1893 | + // which is what lets the body call it. | ||
| 1894 | + if matches!(seg.ident.to_string().as_str(), "Fn" | "FnMut" | "FnOnce") { | ||
| 1895 | + if let syn::PathArguments::Parenthesized(pa) = &seg.arguments { | ||
| 1896 | + let args: Result<Vec<Nim>, String> = | ||
| 1897 | + pa.inputs.iter().map(|x| me.map_ty(&x.ty)).collect(); | ||
| 1898 | + let ret = match &pa.output { | ||
| 1899 | + ReturnType::Default => Ok(Nim::Unit), | ||
| 1900 | + ReturnType::Type(_, t) => me.map_ty(t), | ||
| 1901 | + }; | ||
| 1902 | + if let (Ok(a), Ok(r)) = (args, ret) { | ||
| 1903 | + me.fn_bounds | ||
| 1904 | + .insert(name.clone(), Nim::Proc(a, Box::new(r))); | ||
| 1905 | + } | ||
| 1906 | + } | ||
| 1907 | + continue; | ||
| 1908 | + } | ||
| 1854 | let args: Vec<Nim> = match &seg.arguments { | 1909 | let args: Vec<Nim> = match &seg.arguments { |
| 1855 | syn::PathArguments::AngleBracketed(a) => a | 1910 | syn::PathArguments::AngleBracketed(a) => a |
| 1856 | .args | 1911 | .args |
| @@ -2010,6 +2065,16 @@ impl Lowerer { | |||
| 2010 | Nim::Array(n, e) => Nim::Array(n.clone(), Box::new(Self::subst(e, m))), | 2065 | Nim::Array(n, e) => Nim::Array(n.clone(), Box::new(Self::subst(e, m))), |
| 2011 | Nim::Var(e) => Nim::Var(Box::new(Self::subst(e, m))), | 2066 | Nim::Var(e) => Nim::Var(Box::new(Self::subst(e, m))), |
| 2012 | Nim::Tuple(ts) => Nim::Tuple(ts.iter().map(|x| Self::subst(x, m)).collect()), | 2067 | Nim::Tuple(ts) => Nim::Tuple(ts.iter().map(|x| Self::subst(x, m)).collect()), |
| 2068 | + // A proc type carries parameters too: `F: FnOnce(&Self) -> R` is | ||
| 2069 | + // useless as an expectation if `Self` survives into it. | ||
| 2070 | + Nim::Proc(a, r) => Nim::Proc( | ||
| 2071 | + a.iter().map(|x| Self::subst(x, m)).collect(), | ||
| 2072 | + Box::new(Self::subst(r, m)), | ||
| 2073 | + ), | ||
| 2074 | + Nim::Ptr(e) => Nim::Ptr(Box::new(Self::subst(e, m))), | ||
| 2075 | + Nim::ConstPtr(e) => Nim::ConstPtr(Box::new(Self::subst(e, m))), | ||
| 2076 | + Nim::Iter(e) => Nim::Iter(Box::new(Self::subst(e, m))), | ||
| 2077 | + Nim::Future(e) => Nim::Future(Box::new(Self::subst(e, m))), | ||
| 2013 | other => other.clone(), | 2078 | other => other.clone(), |
| 2014 | } | 2079 | } |
| 2015 | } | 2080 | } |
| @@ -2838,22 +2903,56 @@ impl Lowerer { | |||
| 2838 | Item::Macro(m) if path_name(&m.mac.path) == "macro_rules" => Ok(()), | 2903 | Item::Macro(m) if path_name(&m.mac.path) == "macro_rules" => Ok(()), |
| 2839 | Item::Type(_) => Ok(()), // expanded at every use site | 2904 | Item::Type(_) => Ok(()), // expanded at every use site |
| 2840 | Item::Trait(t) => { | 2905 | Item::Trait(t) => { |
| 2841 | - // We do not model trait resolution, so a declaration generates | 2906 | + // A trait declaration generates nothing by itself. A *default |
| 2842 | - // nothing and a use that needed it is rejected where it | 2907 | + // body* is code, though, and it is code that applies to every |
| 2843 | - // appears. A *default body*, though, is code: dropping it | 2908 | + // implementor -- which is what a Nim generic proc is. |
| 2844 | - // would silently remove a method the impls inherit. | 2909 | + // |
| 2910 | + // Trait methods are already named `rs{Trait}_{method}` and | ||
| 2911 | + // overloaded on the receiver, so a default body emitted with | ||
| 2912 | + // `Self` as a type parameter is the fallback, and an `impl` | ||
| 2913 | + // that overrides it is a more specific overload that Nim | ||
| 2914 | + // prefers. That is Rust's rule, arrived at by Nim's. | ||
| 2915 | + let tname = t.ident.to_string(); | ||
| 2916 | + let tgen = Self::generics_of(&t.generics); | ||
| 2845 | for it in &t.items { | 2917 | for it in &t.items { |
| 2846 | - if let syn::TraitItem::Fn(f) = it { | 2918 | + let syn::TraitItem::Fn(f) = it else { continue }; |
| 2847 | - if f.default.is_some() { | 2919 | + let Some(body) = &f.default else { continue }; |
| 2848 | - return Err(format!( | 2920 | + let outer_g = std::mem::replace(&mut self.impl_generics, { |
| 2849 | - "`trait {}` gives `{}` a default body; trait \ | 2921 | + let mut g = vec!["Self".to_string()]; |
| 2850 | - resolution is not modelled, so that body has no \ | 2922 | + g.extend(tgen.clone()); |
| 2851 | - impl to be emitted into and dropping it would \ | 2923 | + g |
| 2852 | - remove code", | 2924 | + }); |
| 2853 | - t.ident, f.sig.ident | 2925 | + let outer_self = |
| 2854 | - )); | 2926 | + self.self_ty.replace(Nim::Named("Self".into(), Vec::new())); |
| 2927 | + // The method's own bounds: `fn apply<F: FnOnce(Self) -> Res>` | ||
| 2928 | + // is what says `f` can be called. | ||
| 2929 | + self.note_bounds(&f.sig.generics); | ||
| 2930 | + let nim = trait_method_name( | ||
| 2931 | + &tname, | ||
| 2932 | + &tname, | ||
| 2933 | + &f.sig.ident.to_string(), | ||
| 2934 | + takes_self(&f.sig), | ||
| 2935 | + ); | ||
| 2936 | + let recv = takes_self(&f.sig) | ||
| 2937 | + .then(|| Nim::Named("Self".into(), Vec::new())); | ||
| 2938 | + // A return type that is a trait parameter appearing | ||
| 2939 | + // nowhere in the arguments cannot be inferred from a call. | ||
| 2940 | + // Nim's `auto` takes it from the body, which is where | ||
| 2941 | + // Rust's monomorphisation gets it too. | ||
| 2942 | + if let Ok((ptys, rt)) = self.signature(&f.sig) { | ||
| 2943 | + if let Nim::Named(g, a) = &rt { | ||
| 2944 | + if a.is_empty() | ||
| 2945 | + && g != "Self" | ||
| 2946 | + && !ptys.iter().any(|p| Self::mentions_any(p, &[g.clone()])) | ||
| 2947 | + { | ||
| 2948 | + self.ret_override = Some(Nim::Prim("auto".into())); | ||
| 2949 | + } | ||
| 2855 | } | 2950 | } |
| 2856 | } | 2951 | } |
| 2952 | + let r = self.func_named(&nim, &f.sig, body, recv); | ||
| 2953 | + self.self_ty = outer_self; | ||
| 2954 | + self.impl_generics = outer_g; | ||
| 2955 | + r?; | ||
| 2857 | } | 2956 | } |
| 2858 | Ok(()) | 2957 | Ok(()) |
| 2859 | } | 2958 | } |
| @@ -3651,6 +3750,9 @@ impl Lowerer { | |||
| 3651 | self.opaque_hint = self.opaque_ret.get(&opaque_key(sig, body)).cloned(); | 3750 | self.opaque_hint = self.opaque_ret.get(&opaque_key(sig, body)).cloned(); |
| 3652 | let (ptys, ret) = self.signature(sig)?; | 3751 | let (ptys, ret) = self.signature(sig)?; |
| 3653 | self.opaque_hint = None; | 3752 | self.opaque_hint = None; |
| 3753 | + let trim = self.ret_override.is_some(); | ||
| 3754 | + let recv_ty = recv.clone(); | ||
| 3755 | + let ret = self.ret_override.take().unwrap_or(ret); | ||
| 3654 | 3756 | ||
| 3655 | self.push_scope(); | 3757 | self.push_scope(); |
| 3656 | let mut rendered: Vec<String> = Vec::new(); | 3758 | let mut rendered: Vec<String> = Vec::new(); |
| @@ -3705,6 +3807,18 @@ impl Lowerer { | |||
| 3705 | gparams.push(g); | 3807 | gparams.push(g); |
| 3706 | } | 3808 | } |
| 3707 | } | 3809 | } |
| 3810 | + // A parameter that appears in no argument cannot be bound by a call. | ||
| 3811 | + // With an `auto` return there is nothing else to bind it either, so it | ||
| 3812 | + // is not a parameter at all -- it was only ever the return type's name. | ||
| 3813 | + if trim { | ||
| 3814 | + // The receiver counts: `self: Self` is what binds `Self`, and it | ||
| 3815 | + // is not one of `ptys`. | ||
| 3816 | + let mut bound: Vec<Nim> = ptys.clone(); | ||
| 3817 | + if let Some(r) = &recv_ty { | ||
| 3818 | + bound.push(r.clone()); | ||
| 3819 | + } | ||
| 3820 | + gparams.retain(|g| bound.iter().any(|p| Self::mentions_any(p, &[g.clone()]))); | ||
| 3821 | + } | ||
| 3708 | let gens = self.gen_list(&gparams); | 3822 | let gens = self.gen_list(&gparams); |
| 3709 | let outer_fg = std::mem::replace(&mut self.fn_generics, gparams.clone()); | 3823 | let outer_fg = std::mem::replace(&mut self.fn_generics, gparams.clone()); |
| 3710 | let head = if ret == Nim::Unit { | 3824 | let head = if ret == Nim::Unit { |
| @@ -6938,6 +7052,68 @@ impl Lowerer { | |||
| 6938 | format!("(block:\n{}\n{pad}{})", copies.join("\n"), proc_code) | 7052 | format!("(block:\n{}\n{pad}{})", copies.join("\n"), proc_code) |
| 6939 | } | 7053 | } |
| 6940 | 7054 | ||
| 7055 | + /// A method the receiver's type does not declare, but a trait it | ||
| 7056 | + /// implements does -- an inherited default body. | ||
| 7057 | + /// | ||
| 7058 | + /// Those are emitted as generic procs under the trait's name, so the call | ||
| 7059 | + /// is the same one an overriding impl would answer; Nim picks the more | ||
| 7060 | + /// specific overload where there is one, which is Rust's rule. | ||
| 7061 | + fn inherited_default( | ||
| 7062 | + &mut self, | ||
| 7063 | + m: &syn::ExprMethodCall, | ||
| 7064 | + name: &str, | ||
| 7065 | + recv: &Val, | ||
| 7066 | + ) -> Result<Option<Val>, String> { | ||
| 7067 | + let Some(rt) = recv.ty.clone() else { return Ok(None) }; | ||
| 7068 | + let rn = type_name(&rt.clone().unvar()); | ||
| 7069 | + if self.fn_generics.iter().any(|g| *g == rn) { | ||
| 7070 | + return Ok(None); | ||
| 7071 | + } | ||
| 7072 | + if self.methods.contains_key(&(rn.clone(), name.to_string())) { | ||
| 7073 | + return Ok(None); | ||
| 7074 | + } | ||
| 7075 | + let trs = self.type_traits.get(&rn).cloned().unwrap_or_default(); | ||
| 7076 | + let Some((tr, targs)) = trs | ||
| 7077 | + .iter() | ||
| 7078 | + .find(|(tr, _)| self.trait_methods.contains_key(&(tr.clone(), name.to_string()))) | ||
| 7079 | + else { | ||
| 7080 | + return Ok(None); | ||
| 7081 | + }; | ||
| 7082 | + let sig = self.trait_methods[&(tr.clone(), name.to_string())].clone(); | ||
| 7083 | + // `Self` is the receiver's type here, and the trait's own parameters | ||
| 7084 | + // are whatever the `impl` supplied for them. | ||
| 7085 | + let mut sm: HashMap<String, Nim> = HashMap::new(); | ||
| 7086 | + sm.insert("Self".into(), rt.clone().unvar()); | ||
| 7087 | + for (p, a) in sig.generics.iter().zip(targs.iter()) { | ||
| 7088 | + sm.insert(p.clone(), a.clone()); | ||
| 7089 | + } | ||
| 7090 | + let mut codes = vec![recv.code.clone()]; | ||
| 7091 | + for (i, a) in m.args.iter().enumerate() { | ||
| 7092 | + let raw = sig.params.get(i + 1).cloned(); | ||
| 7093 | + // A parameter that is a callable generic says what shape a | ||
| 7094 | + // closure written here has. | ||
| 7095 | + let want = match &raw { | ||
| 7096 | + Some(Nim::Named(g, _)) if self.fn_bounds.contains_key(g) => { | ||
| 7097 | + Some(Self::subst(&self.fn_bounds[g].clone(), &sm)) | ||
| 7098 | + } | ||
| 7099 | + Some(t) if !Self::mentions_any(t, &sig.generics) && type_name(t) != "Self" => { | ||
| 7100 | + Some(t.clone()) | ||
| 7101 | + } | ||
| 7102 | + _ => None, | ||
| 7103 | + }; | ||
| 7104 | + codes.push(self.expr_at(a, want.as_ref())?.code); | ||
| 7105 | + } | ||
| 7106 | + let ret = Self::subst(&sig.ret, &sm); | ||
| 7107 | + Ok(Some(Val::new( | ||
| 7108 | + format!( | ||
| 7109 | + "{}({})", | ||
| 7110 | + trait_method_name(&rn, tr, name, true), | ||
| 7111 | + codes.join(", ") | ||
| 7112 | + ), | ||
| 7113 | + Some(ret), | ||
| 7114 | + ))) | ||
| 7115 | + } | ||
| 7116 | + | ||
| 6941 | /// `|x| x + 1` -> a Nim anonymous proc. | 7117 | /// `|x| x + 1` -> a Nim anonymous proc. |
| 6942 | /// | 7118 | /// |
| 6943 | /// Nim's closures capture by reference, as Rust's non-`move` closures do. | 7119 | /// Nim's closures capture by reference, as Rust's non-`move` closures do. |
| @@ -7768,7 +7944,13 @@ impl Lowerer { | |||
| 7768 | // conversion, e.g. `String::from(..)`; handled by the method path. | 7944 | // conversion, e.g. `String::from(..)`; handled by the method path. |
| 7769 | // Calling a proc-typed local, which is how an `impl Fn(..)` parameter | 7945 | // Calling a proc-typed local, which is how an `impl Fn(..)` parameter |
| 7770 | // is invoked. | 7946 | // is invoked. |
| 7771 | - if let Some(Nim::Proc(ptys, ret)) = self.lookup(&name) { | 7947 | + // A local whose type is a generic parameter bound by `Fn`: the bound |
| 7948 | + // says it is callable, and with what signature. | ||
| 7949 | + let callee_ty = match self.lookup(&name) { | ||
| 7950 | + Some(Nim::Named(g, _)) => self.fn_bounds.get(&g).cloned(), | ||
| 7951 | + other => other, | ||
| 7952 | + }; | ||
| 7953 | + if let Some(Nim::Proc(ptys, ret)) = callee_ty { | ||
| 7772 | // Re-lowered against the proc's own parameter types: a literal | 7954 | // Re-lowered against the proc's own parameter types: a literal |
| 7773 | // argument takes its width from them, and nothing earlier in this | 7955 | // argument takes its width from them, and nothing earlier in this |
| 7774 | // function knew them. | 7956 | // function knew them. |
| @@ -8073,6 +8255,14 @@ impl Lowerer { | |||
| 8073 | ("push", Some(Nim::Seq(t))) | ("push", Some(Nim::OpenArray(t))) => Some((**t).clone()), | 8255 | ("push", Some(Nim::Seq(t))) | ("push", Some(Nim::OpenArray(t))) => Some((**t).clone()), |
| 8074 | (_, t) => t.clone(), | 8256 | (_, t) => t.clone(), |
| 8075 | }; | 8257 | }; |
| 8258 | + // A method the receiver's type does not declare, but a trait it | ||
| 8259 | + // implements does -- an inherited default body. Resolved before the | ||
| 8260 | + // arguments are lowered, because the trait's signature is what says | ||
| 8261 | + // their types: a closure argument has no shape without it. | ||
| 8262 | + if let Some(v) = self.inherited_default(m, &name, &recv)? { | ||
| 8263 | + return Ok(v); | ||
| 8264 | + } | ||
| 8265 | + | ||
| 8076 | let mut args = Vec::new(); | 8266 | let mut args = Vec::new(); |
| 8077 | for a in &m.args { | 8267 | for a in &m.args { |
| 8078 | args.push(self.expr_at(a, arg_want.as_ref())?); | 8268 | args.push(self.expr_at(a, arg_want.as_ref())?); |
added
tests/cases/071-trait-default-body.rs +57 -0 | new file mode 100644 | ||
| @@ -0,0 +1,57 @@ | ||
| 1 | +// A trait's default body is code that applies to every implementor, which is | |
| 2 | +// what a Nim generic proc is. An `impl` that overrides it is a more specific | |
| 3 | +// overload, which Nim prefers — Rust's rule, arrived at by Nim's. | |
| 4 | +trait Describe { | |
| 5 | + fn name(&self) -> String; | |
| 6 | + | |
| 7 | + // Default: used by anything that does not override it. | |
| 8 | + fn describe(&self) -> String { | |
| 9 | + format!("<{}>", self.name()) | |
| 10 | + } | |
| 11 | + | |
| 12 | + fn shout(&self) -> String { | |
| 13 | + format!("{}!", self.describe()) | |
| 14 | + } | |
| 15 | +} | |
| 16 | + | |
| 17 | +struct Dog; | |
| 18 | +struct Cat; | |
| 19 | + | |
| 20 | +impl Describe for Dog { | |
| 21 | + fn name(&self) -> String { | |
| 22 | + String::from("dog") | |
| 23 | + } | |
| 24 | +} | |
| 25 | + | |
| 26 | +impl Describe for Cat { | |
| 27 | + fn name(&self) -> String { | |
| 28 | + String::from("cat") | |
| 29 | + } | |
| 30 | + // Overrides the default. | |
| 31 | + fn describe(&self) -> String { | |
| 32 | + String::from("[cat]") | |
| 33 | + } | |
| 34 | +} | |
| 35 | + | |
| 36 | +// A default body that calls a function it was handed. | |
| 37 | +trait Apply<R> { | |
| 38 | + fn apply<F: FnOnce(&Self) -> R>(&self, f: F) -> R { | |
| 39 | + f(self) | |
| 40 | + } | |
| 41 | +} | |
| 42 | + | |
| 43 | +struct Num { | |
| 44 | + v: u32, | |
| 45 | +} | |
| 46 | + | |
| 47 | +impl Apply<u32> for Num {} | |
| 48 | + | |
| 49 | +fn main() { | |
| 50 | + let d = Dog; | |
| 51 | + let c = Cat; | |
| 52 | + println!("{} {}", d.describe(), d.shout()); | |
| 53 | + println!("{} {}", c.describe(), c.shout()); | |
| 54 | + | |
| 55 | + let n = Num { v: 20 }; | |
| 56 | + println!("{}", n.apply(|x| x.v * 2)); | |
| 57 | +} | |
| new file mode 100644 | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | +// A trait's default body is code that applies to every implementor, which is | ||
| 2 | +// what a Nim generic proc is. An `impl` that overrides it is a more specific | ||
| 3 | +// overload, which Nim prefers — Rust's rule, arrived at by Nim's. | ||
| 4 | +trait Describe { | ||
| 5 | + fn name(&self) -> String; | ||
| 6 | + | ||
| 7 | + // Default: used by anything that does not override it. | ||
| 8 | + fn describe(&self) -> String { | ||
| 9 | + format!("<{}>", self.name()) | ||
| 10 | + } | ||
| 11 | + | ||
| 12 | + fn shout(&self) -> String { | ||
| 13 | + format!("{}!", self.describe()) | ||
| 14 | + } | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +struct Dog; | ||
| 18 | +struct Cat; | ||
| 19 | + | ||
| 20 | +impl Describe for Dog { | ||
| 21 | + fn name(&self) -> String { | ||
| 22 | + String::from("dog") | ||
| 23 | + } | ||
| 24 | +} | ||
| 25 | + | ||
| 26 | +impl Describe for Cat { | ||
| 27 | + fn name(&self) -> String { | ||
| 28 | + String::from("cat") | ||
| 29 | + } | ||
| 30 | + // Overrides the default. | ||
| 31 | + fn describe(&self) -> String { | ||
| 32 | + String::from("[cat]") | ||
| 33 | + } | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +// A default body that calls a function it was handed. | ||
| 37 | +trait Apply<R> { | ||
| 38 | + fn apply<F: FnOnce(&Self) -> R>(&self, f: F) -> R { | ||
| 39 | + f(self) | ||
| 40 | + } | ||
| 41 | +} | ||
| 42 | + | ||
| 43 | +struct Num { | ||
| 44 | + v: u32, | ||
| 45 | +} | ||
| 46 | + | ||
| 47 | +impl Apply<u32> for Num {} | ||
| 48 | + | ||
| 49 | +fn main() { | ||
| 50 | + let d = Dog; | ||
| 51 | + let c = Cat; | ||
| 52 | + println!("{} {}", d.describe(), d.shout()); | ||
| 53 | + println!("{} {}", c.describe(), c.shout()); | ||
| 54 | + | ||
| 55 | + let n = Num { v: 20 }; | ||
| 56 | + println!("{}", n.apply(|x| x.v * 2)); | ||
| 57 | +} | ||