nandi/rustnimpublic Fork
⑂ f3e08c6
Commits
⬇ Clone ▾
git clone https://git.rickub.com/nandi/rustnim.git
git clone ssh://git@rickub.com/nandi/rustnim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Download ZIP

Dispatch a method call on a generic parameter through its trait bound

`alternatives.choice(i)` where `Alternatives: Alt<Input, Output, Error>` is
how every winnow combinator is written, and rustnim had no way to type it:
traits were collected for nothing but a default-body check, and nothing
recorded which bounds a generic parameter carried.

Both are recorded now, and the call resolves through them. The lowering needs
no dispatch mechanism of its own: trait methods with a receiver were already
named `rs{Trait}_{method}` and overloaded on the receiver type, so Nim picks
the right one at instantiation -- which is exactly where Rust picks it, at
monomorphisation. The bound is what lets rustnim *type* the call in between,
and the trait's declared signature gives the return type once the bound's
arguments are substituted in.

`opaque_of` also has to put the signature's type parameters in scope, or a
call on one of them is not recognised as a call on a generic at all. That was
enough to resolve `alt` and the combinators shaped like it.

`Sig` derives Clone; it was cloned by reference before, which the borrow
checker was right to object to.

tests/cases/060, with the bound written both on the parameter list and in a
`where` clause.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-19T19:16:29-07:00 Browse files
f3e08c6 parent: 11fe65c
modified src/lower.rs +138 -2
@@ -185,6 +185,7 @@ impl Val {
185185 }
186186 }
187187
188+#[derive(Clone)]
188189 struct Sig {
189190 params: Vec<Nim>,
190191 ret: Nim,
@@ -347,6 +348,14 @@ pub struct Lowerer {
347348 /// Const generic parameters, by name, with their Nim type. Nim's `static`
348349 /// parameter is the same thing: a generic parameter that is a value.
349350 const_gens: HashMap<String, String>,
351+ /// Methods a `trait` declares, by `(trait, method)`, with the trait's own
352+ /// generic parameters. Used to type a call on a value whose type is a
353+ /// generic parameter: `alternatives.choice(i)` where the parameter is
354+ /// bound by `Alt<I, O, E>`.
355+ trait_methods: HashMap<(String, String), Sig>,
356+ /// The trait bounds on each generic parameter in scope, as `(trait,
357+ /// arguments)`.
358+ bounds: HashMap<String, Vec<(String, Vec<Nim>)>>,
350359 /// Combined table size when `CONST_NAMES` was last rebuilt.
351360 const_names_at: std::cell::Cell<Option<usize>>,
352361 /// Bodies of functions whose return type is a return-position `impl
@@ -437,6 +446,8 @@ impl Lowerer {
437446 emitted_local: std::collections::HashSet::new(),
438447 emitted_assoc: std::collections::HashSet::new(),
439448 const_gens: HashMap::new(),
449+ trait_methods: HashMap::new(),
450+ bounds: HashMap::new(),
440451 const_names_at: std::cell::Cell::new(None),
441452 opaque_fns: Vec::new(),
442453 opaque_ret: HashMap::new(),
@@ -855,11 +866,35 @@ impl Lowerer {
855866 return Ok(());
856867 }
857868 match item {
869+ Item::Trait(t) => {
870+ // The declarations, so a call on a generic parameter bound by
871+ // this trait can be typed. Nim resolves such a call at
872+ // instantiation, the way Rust resolves it at monomorphisation,
873+ // so what is needed here is the *signature*, not a body.
874+ let tname = t.ident.to_string();
875+ let tgen = Self::generics_of(&t.generics);
876+ let outer = std::mem::replace(&mut self.impl_generics, tgen.clone());
877+ for it in &t.items {
878+ if let syn::TraitItem::Fn(f) = it {
879+ if let Ok((mut params, ret)) = self.signature(&f.sig) {
880+ if takes_self(&f.sig) {
881+ params.insert(0, Nim::Named("Self".into(), Vec::new()));
882+ }
883+ self.trait_methods.insert(
884+ (tname.clone(), f.sig.ident.to_string()),
885+ Sig { params, ret, generics: tgen.clone() },
886+ );
887+ }
888+ }
889+ }
890+ self.impl_generics = outer;
891+ }
858892 Item::Fn(f) => {
859893 // A body may declare its own types, and a signature below may
860894 // name one, so these come first.
861895 self.collect_body_items(&f.block)?;
862896 self.note_const_generics(&f.sig.generics)?;
897+ self.note_bounds(&f.sig.generics);
863898 let (params, ret) = self.signature(&f.sig)?;
864899 if matches!(ret, Nim::Future(_)) {
865900 self.uses_async = true;
@@ -1033,6 +1068,7 @@ impl Lowerer {
10331068 // Before anything else: a method body may declare its own types, and
10341069 // the trait branches below return early.
10351070 self.note_const_generics(&im.generics)?;
1071+ self.note_bounds(&im.generics);
10361072 // Associated consts are registered here for the same reason anything
10371073 // else is: an array length elsewhere may name one, and that name has
10381074 // to resolve before any body is lowered. `assoc_const` records them
@@ -1043,6 +1079,7 @@ impl Lowerer {
10431079 syn::ImplItem::Fn(m) => {
10441080 self.collect_body_items(&m.block)?;
10451081 self.note_const_generics(&m.sig.generics)?;
1082+ self.note_bounds(&m.sig.generics);
10461083 }
10471084 syn::ImplItem::Const(c) => {
10481085 let t = self.map_ty(&c.ty)?.owned();
@@ -1523,6 +1560,55 @@ impl Lowerer {
15231560 /// program mean something different — it only makes rustnim accept some
15241561 /// programs rustc would have rejected, which does not matter when the
15251562 /// input is known-good Rust.
1563+ /// Record the trait bounds on a signature's generic parameters, from both
1564+ /// the parameter list and the `where` clause.
1565+ ///
1566+ /// These are what says that `alternatives.choice(i)` is legal: `choice`
1567+ /// belongs to `Alt`, and the parameter is bound by it. Rust checks the
1568+ /// bound and then monomorphises; Nim's generics resolve the same call at
1569+ /// instantiation without a bound to check. The bound is what lets rustnim
1570+ /// *type* the call in between.
1571+ fn note_bounds(&mut self, g: &syn::Generics) {
1572+ let mut add = |me: &mut Self, name: String, bounds: &syn::punctuated::Punctuated<syn::TypeParamBound, syn::Token![+]>| {
1573+ for b in bounds {
1574+ let syn::TypeParamBound::Trait(tb) = b else { continue };
1575+ let Some(seg) = tb.path.segments.last() else { continue };
1576+ let args: Vec<Nim> = match &seg.arguments {
1577+ syn::PathArguments::AngleBracketed(a) => a
1578+ .args
1579+ .iter()
1580+ .filter_map(|x| match x {
1581+ GenericArgument::Type(t) => me.map_ty(t).ok(),
1582+ _ => None,
1583+ })
1584+ .collect(),
1585+ _ => Vec::new(),
1586+ };
1587+ me.bounds
1588+ .entry(name.clone())
1589+ .or_default()
1590+ .push((seg.ident.to_string(), args));
1591+ }
1592+ };
1593+ for p in &g.params {
1594+ if let syn::GenericParam::Type(t) = p {
1595+ let n = t.ident.to_string();
1596+ add(self, n, &t.bounds);
1597+ }
1598+ }
1599+ if let Some(w) = &g.where_clause {
1600+ for pred in &w.predicates {
1601+ let syn::WherePredicate::Type(pt) = pred else { continue };
1602+ let syn::Type::Path(tp) = &pt.bounded_ty else { continue };
1603+ if tp.path.segments.len() != 1 {
1604+ continue;
1605+ }
1606+ let n = tp.path.segments[0].ident.to_string();
1607+ add(self, n, &pt.bounds);
1608+ }
1609+ }
1610+ }
1611+
15261612 fn generics_of(g: &syn::Generics) -> Vec<String> {
15271613 g.params
15281614 .iter()
@@ -2116,6 +2202,16 @@ impl Lowerer {
21162202 }
21172203 let outer_self = self.self_ty.clone();
21182204 self.self_ty = self_ty.cloned().or(outer_self.clone());
2205+ // The signature's type parameters have to be in scope, or a method
2206+ // call on one of them is not recognised as a call on a generic.
2207+ let mut gp = self.impl_generics.clone();
2208+ for g in Self::generics_of(&sig.generics) {
2209+ if !gp.contains(&g) {
2210+ gp.push(g);
2211+ }
2212+ }
2213+ gp.extend(self.impl_trait_params(sig));
2214+ let outer_fg = std::mem::replace(&mut self.fn_generics, gp);
21192215 let mut i = 0usize;
21202216 for a in &sig.inputs {
21212217 if let FnArg::Typed(t) = a {
@@ -2140,6 +2236,7 @@ impl Lowerer {
21402236 self.hoisted.truncate(saved_hoisted);
21412237 self.pop_scope();
21422238 self.self_ty = outer_self;
2239+ self.fn_generics = outer_fg;
21432240 found
21442241 }
21452242
@@ -6977,10 +7074,49 @@ impl Lowerer {
69777074 // Nim at instantiation, so the call just needs the right proc
69787075 // name. Every impl of a trait method shares one, so any
69797076 // candidate with this method name gives it.
6980- let generic_recv = rt
7077+ let recv_param = rt
69817078 .as_ref()
69827079 .map(type_name)
6983- .is_some_and(|n| self.fn_generics.iter().any(|g| *g == n));
7080+ .filter(|n| self.fn_generics.iter().any(|g| g == n));
7081+
7082+ // The receiver's type is a generic parameter and one of its
7083+ // trait bounds declares this method. That bound is what makes
7084+ // the call legal in Rust, and it names the proc: every impl of
7085+ // a trait method shares one name and overloads on the
7086+ // receiver, so Nim picks the right one at instantiation --
7087+ // which is where Rust picks it too.
7088+ if let Some(param) = &recv_param {
7089+ let bs = self.bounds.get(param).cloned().unwrap_or_default();
7090+ let mut found = None;
7091+ for (tr, targs) in &bs {
7092+ if let Some(sig) = self.trait_methods.get(&(tr.clone(), name.clone())) {
7093+ found = Some((tr.clone(), targs.clone(), sig.clone()));
7094+ break;
7095+ }
7096+ }
7097+ if let Some((tr, targs, sig)) = found {
7098+ let mut sm: HashMap<String, Nim> = HashMap::new();
7099+ for (p, a) in sig.generics.iter().zip(targs.iter()) {
7100+ sm.insert(p.clone(), a.clone());
7101+ }
7102+ let mut codes = vec![recv.code.clone()];
7103+ for (i, a) in m.args.iter().enumerate() {
7104+ let want = sig
7105+ .params
7106+ .get(i + 1)
7107+ .map(|t| Self::subst(t, &sm))
7108+ .filter(|t| !Self::mentions_any(t, &sig.generics));
7109+ codes.push(self.expr_at(a, want.as_ref())?.code);
7110+ }
7111+ let ret = Self::subst(&sig.ret, &sm);
7112+ return Ok(Val::new(
7113+ format!("{}({})", trait_method_name(param, &tr, &name, true), codes.join(", ")),
7114+ Some(ret),
7115+ ));
7116+ }
7117+ }
7118+
7119+ let generic_recv = recv_param.is_some();
69847120 let key = if generic_recv {
69857121 let mut names: Vec<String> = self
69867122 .statics
@@ -185,6 +185,7 @@ impl Val {
185 }185 }
186 }186 }
187 187
188+#[derive(Clone)]
188 struct Sig {189 struct Sig {
189 params: Vec<Nim>,190 params: Vec<Nim>,
190 ret: Nim,191 ret: Nim,
@@ -347,6 +348,14 @@ pub struct Lowerer {
347 /// Const generic parameters, by name, with their Nim type. Nim's `static`348 /// Const generic parameters, by name, with their Nim type. Nim's `static`
348 /// parameter is the same thing: a generic parameter that is a value.349 /// parameter is the same thing: a generic parameter that is a value.
349 const_gens: HashMap<String, String>,350 const_gens: HashMap<String, String>,
351+ /// Methods a `trait` declares, by `(trait, method)`, with the trait's own
352+ /// generic parameters. Used to type a call on a value whose type is a
353+ /// generic parameter: `alternatives.choice(i)` where the parameter is
354+ /// bound by `Alt<I, O, E>`.
355+ trait_methods: HashMap<(String, String), Sig>,
356+ /// The trait bounds on each generic parameter in scope, as `(trait,
357+ /// arguments)`.
358+ bounds: HashMap<String, Vec<(String, Vec<Nim>)>>,
350 /// Combined table size when `CONST_NAMES` was last rebuilt.359 /// Combined table size when `CONST_NAMES` was last rebuilt.
351 const_names_at: std::cell::Cell<Option<usize>>,360 const_names_at: std::cell::Cell<Option<usize>>,
352 /// Bodies of functions whose return type is a return-position `impl361 /// Bodies of functions whose return type is a return-position `impl
@@ -437,6 +446,8 @@ impl Lowerer {
437 emitted_local: std::collections::HashSet::new(),446 emitted_local: std::collections::HashSet::new(),
438 emitted_assoc: std::collections::HashSet::new(),447 emitted_assoc: std::collections::HashSet::new(),
439 const_gens: HashMap::new(),448 const_gens: HashMap::new(),
449+ trait_methods: HashMap::new(),
450+ bounds: HashMap::new(),
440 const_names_at: std::cell::Cell::new(None),451 const_names_at: std::cell::Cell::new(None),
441 opaque_fns: Vec::new(),452 opaque_fns: Vec::new(),
442 opaque_ret: HashMap::new(),453 opaque_ret: HashMap::new(),
@@ -855,11 +866,35 @@ impl Lowerer {
855 return Ok(());866 return Ok(());
856 }867 }
857 match item {868 match item {
869+ Item::Trait(t) => {
870+ // The declarations, so a call on a generic parameter bound by
871+ // this trait can be typed. Nim resolves such a call at
872+ // instantiation, the way Rust resolves it at monomorphisation,
873+ // so what is needed here is the *signature*, not a body.
874+ let tname = t.ident.to_string();
875+ let tgen = Self::generics_of(&t.generics);
876+ let outer = std::mem::replace(&mut self.impl_generics, tgen.clone());
877+ for it in &t.items {
878+ if let syn::TraitItem::Fn(f) = it {
879+ if let Ok((mut params, ret)) = self.signature(&f.sig) {
880+ if takes_self(&f.sig) {
881+ params.insert(0, Nim::Named("Self".into(), Vec::new()));
882+ }
883+ self.trait_methods.insert(
884+ (tname.clone(), f.sig.ident.to_string()),
885+ Sig { params, ret, generics: tgen.clone() },
886+ );
887+ }
888+ }
889+ }
890+ self.impl_generics = outer;
891+ }
858 Item::Fn(f) => {892 Item::Fn(f) => {
859 // A body may declare its own types, and a signature below may893 // A body may declare its own types, and a signature below may
860 // name one, so these come first.894 // name one, so these come first.
861 self.collect_body_items(&f.block)?;895 self.collect_body_items(&f.block)?;
862 self.note_const_generics(&f.sig.generics)?;896 self.note_const_generics(&f.sig.generics)?;
897+ self.note_bounds(&f.sig.generics);
863 let (params, ret) = self.signature(&f.sig)?;898 let (params, ret) = self.signature(&f.sig)?;
864 if matches!(ret, Nim::Future(_)) {899 if matches!(ret, Nim::Future(_)) {
865 self.uses_async = true;900 self.uses_async = true;
@@ -1033,6 +1068,7 @@ impl Lowerer {
1033 // Before anything else: a method body may declare its own types, and1068 // Before anything else: a method body may declare its own types, and
1034 // the trait branches below return early.1069 // the trait branches below return early.
1035 self.note_const_generics(&im.generics)?;1070 self.note_const_generics(&im.generics)?;
1071+ self.note_bounds(&im.generics);
1036 // Associated consts are registered here for the same reason anything1072 // Associated consts are registered here for the same reason anything
1037 // else is: an array length elsewhere may name one, and that name has1073 // else is: an array length elsewhere may name one, and that name has
1038 // to resolve before any body is lowered. `assoc_const` records them1074 // to resolve before any body is lowered. `assoc_const` records them
@@ -1043,6 +1079,7 @@ impl Lowerer {
1043 syn::ImplItem::Fn(m) => {1079 syn::ImplItem::Fn(m) => {
1044 self.collect_body_items(&m.block)?;1080 self.collect_body_items(&m.block)?;
1045 self.note_const_generics(&m.sig.generics)?;1081 self.note_const_generics(&m.sig.generics)?;
1082+ self.note_bounds(&m.sig.generics);
1046 }1083 }
1047 syn::ImplItem::Const(c) => {1084 syn::ImplItem::Const(c) => {
1048 let t = self.map_ty(&c.ty)?.owned();1085 let t = self.map_ty(&c.ty)?.owned();
@@ -1523,6 +1560,55 @@ impl Lowerer {
1523 /// program mean something different — it only makes rustnim accept some1560 /// program mean something different — it only makes rustnim accept some
1524 /// programs rustc would have rejected, which does not matter when the1561 /// programs rustc would have rejected, which does not matter when the
1525 /// input is known-good Rust.1562 /// input is known-good Rust.
1563+ /// Record the trait bounds on a signature's generic parameters, from both
1564+ /// the parameter list and the `where` clause.
1565+ ///
1566+ /// These are what says that `alternatives.choice(i)` is legal: `choice`
1567+ /// belongs to `Alt`, and the parameter is bound by it. Rust checks the
1568+ /// bound and then monomorphises; Nim's generics resolve the same call at
1569+ /// instantiation without a bound to check. The bound is what lets rustnim
1570+ /// *type* the call in between.
1571+ fn note_bounds(&mut self, g: &syn::Generics) {
1572+ let mut add = |me: &mut Self, name: String, bounds: &syn::punctuated::Punctuated<syn::TypeParamBound, syn::Token![+]>| {
1573+ for b in bounds {
1574+ let syn::TypeParamBound::Trait(tb) = b else { continue };
1575+ let Some(seg) = tb.path.segments.last() else { continue };
1576+ let args: Vec<Nim> = match &seg.arguments {
1577+ syn::PathArguments::AngleBracketed(a) => a
1578+ .args
1579+ .iter()
1580+ .filter_map(|x| match x {
1581+ GenericArgument::Type(t) => me.map_ty(t).ok(),
1582+ _ => None,
1583+ })
1584+ .collect(),
1585+ _ => Vec::new(),
1586+ };
1587+ me.bounds
1588+ .entry(name.clone())
1589+ .or_default()
1590+ .push((seg.ident.to_string(), args));
1591+ }
1592+ };
1593+ for p in &g.params {
1594+ if let syn::GenericParam::Type(t) = p {
1595+ let n = t.ident.to_string();
1596+ add(self, n, &t.bounds);
1597+ }
1598+ }
1599+ if let Some(w) = &g.where_clause {
1600+ for pred in &w.predicates {
1601+ let syn::WherePredicate::Type(pt) = pred else { continue };
1602+ let syn::Type::Path(tp) = &pt.bounded_ty else { continue };
1603+ if tp.path.segments.len() != 1 {
1604+ continue;
1605+ }
1606+ let n = tp.path.segments[0].ident.to_string();
1607+ add(self, n, &pt.bounds);
1608+ }
1609+ }
1610+ }
1611+
1526 fn generics_of(g: &syn::Generics) -> Vec<String> {1612 fn generics_of(g: &syn::Generics) -> Vec<String> {
1527 g.params1613 g.params
1528 .iter()1614 .iter()
@@ -2116,6 +2202,16 @@ impl Lowerer {
2116 }2202 }
2117 let outer_self = self.self_ty.clone();2203 let outer_self = self.self_ty.clone();
2118 self.self_ty = self_ty.cloned().or(outer_self.clone());2204 self.self_ty = self_ty.cloned().or(outer_self.clone());
2205+ // The signature's type parameters have to be in scope, or a method
2206+ // call on one of them is not recognised as a call on a generic.
2207+ let mut gp = self.impl_generics.clone();
2208+ for g in Self::generics_of(&sig.generics) {
2209+ if !gp.contains(&g) {
2210+ gp.push(g);
2211+ }
2212+ }
2213+ gp.extend(self.impl_trait_params(sig));
2214+ let outer_fg = std::mem::replace(&mut self.fn_generics, gp);
2119 let mut i = 0usize;2215 let mut i = 0usize;
2120 for a in &sig.inputs {2216 for a in &sig.inputs {
2121 if let FnArg::Typed(t) = a {2217 if let FnArg::Typed(t) = a {
@@ -2140,6 +2236,7 @@ impl Lowerer {
2140 self.hoisted.truncate(saved_hoisted);2236 self.hoisted.truncate(saved_hoisted);
2141 self.pop_scope();2237 self.pop_scope();
2142 self.self_ty = outer_self;2238 self.self_ty = outer_self;
2239+ self.fn_generics = outer_fg;
2143 found2240 found
2144 }2241 }
2145 2242
@@ -6977,10 +7074,49 @@ impl Lowerer {
6977 // Nim at instantiation, so the call just needs the right proc7074 // Nim at instantiation, so the call just needs the right proc
6978 // name. Every impl of a trait method shares one, so any7075 // name. Every impl of a trait method shares one, so any
6979 // candidate with this method name gives it.7076 // candidate with this method name gives it.
6980- let generic_recv = rt7077+ let recv_param = rt
6981 .as_ref()7078 .as_ref()
6982 .map(type_name)7079 .map(type_name)
6983- .is_some_and(|n| self.fn_generics.iter().any(|g| *g == n));7080+ .filter(|n| self.fn_generics.iter().any(|g| g == n));
7081+
7082+ // The receiver's type is a generic parameter and one of its
7083+ // trait bounds declares this method. That bound is what makes
7084+ // the call legal in Rust, and it names the proc: every impl of
7085+ // a trait method shares one name and overloads on the
7086+ // receiver, so Nim picks the right one at instantiation --
7087+ // which is where Rust picks it too.
7088+ if let Some(param) = &recv_param {
7089+ let bs = self.bounds.get(param).cloned().unwrap_or_default();
7090+ let mut found = None;
7091+ for (tr, targs) in &bs {
7092+ if let Some(sig) = self.trait_methods.get(&(tr.clone(), name.clone())) {
7093+ found = Some((tr.clone(), targs.clone(), sig.clone()));
7094+ break;
7095+ }
7096+ }
7097+ if let Some((tr, targs, sig)) = found {
7098+ let mut sm: HashMap<String, Nim> = HashMap::new();
7099+ for (p, a) in sig.generics.iter().zip(targs.iter()) {
7100+ sm.insert(p.clone(), a.clone());
7101+ }
7102+ let mut codes = vec![recv.code.clone()];
7103+ for (i, a) in m.args.iter().enumerate() {
7104+ let want = sig
7105+ .params
7106+ .get(i + 1)
7107+ .map(|t| Self::subst(t, &sm))
7108+ .filter(|t| !Self::mentions_any(t, &sig.generics));
7109+ codes.push(self.expr_at(a, want.as_ref())?.code);
7110+ }
7111+ let ret = Self::subst(&sig.ret, &sm);
7112+ return Ok(Val::new(
7113+ format!("{}({})", trait_method_name(param, &tr, &name, true), codes.join(", ")),
7114+ Some(ret),
7115+ ));
7116+ }
7117+ }
7118+
7119+ let generic_recv = recv_param.is_some();
6984 let key = if generic_recv {7120 let key = if generic_recv {
6985 let mut names: Vec<String> = self7121 let mut names: Vec<String> = self
6986 .statics7122 .statics
added tests/cases/060-generic-trait-dispatch.rs +56 -0
new file mode 100644
@@ -0,0 +1,56 @@
1+// A method called on a value whose type is a generic parameter. The trait
2+// bound is what makes the call legal in Rust, and it is what names the proc
3+// here: every impl of a trait method shares one name and overloads on the
4+// receiver, so Nim resolves the call at instantiation — which is where Rust
5+// resolves it too.
6+trait Shape {
7+ fn area(&self) -> u32;
8+ fn scaled(&self, by: u32) -> u32;
9+}
10+
11+struct Square {
12+ side: u32,
13+}
14+
15+struct Rect {
16+ w: u32,
17+ h: u32,
18+}
19+
20+impl Shape for Square {
21+ fn area(&self) -> u32 {
22+ self.side * self.side
23+ }
24+ fn scaled(&self, by: u32) -> u32 {
25+ self.area() * by
26+ }
27+}
28+
29+impl Shape for Rect {
30+ fn area(&self) -> u32 {
31+ self.w * self.h
32+ }
33+ fn scaled(&self, by: u32) -> u32 {
34+ self.area() * by
35+ }
36+}
37+
38+// The bound is on the parameter list.
39+fn describe<S: Shape>(s: &S) -> u32 {
40+ s.area()
41+}
42+
43+// The bound is in a `where` clause, which is the same thing said elsewhere.
44+fn twice<S>(s: &S) -> u32
45+where
46+ S: Shape,
47+{
48+ s.scaled(2)
49+}
50+
51+fn main() {
52+ let sq = Square { side: 4 };
53+ let r = Rect { w: 3, h: 5 };
54+ println!("{} {}", describe(&sq), describe(&r));
55+ println!("{} {}", twice(&sq), twice(&r));
56+}
new file mode 100644
@@ -0,0 +1,56 @@
1+// A method called on a value whose type is a generic parameter. The trait
2+// bound is what makes the call legal in Rust, and it is what names the proc
3+// here: every impl of a trait method shares one name and overloads on the
4+// receiver, so Nim resolves the call at instantiation — which is where Rust
5+// resolves it too.
6+trait Shape {
7+ fn area(&self) -> u32;
8+ fn scaled(&self, by: u32) -> u32;
9+}
10+
11+struct Square {
12+ side: u32,
13+}
14+
15+struct Rect {
16+ w: u32,
17+ h: u32,
18+}
19+
20+impl Shape for Square {
21+ fn area(&self) -> u32 {
22+ self.side * self.side
23+ }
24+ fn scaled(&self, by: u32) -> u32 {
25+ self.area() * by
26+ }
27+}
28+
29+impl Shape for Rect {
30+ fn area(&self) -> u32 {
31+ self.w * self.h
32+ }
33+ fn scaled(&self, by: u32) -> u32 {
34+ self.area() * by
35+ }
36+}
37+
38+// The bound is on the parameter list.
39+fn describe<S: Shape>(s: &S) -> u32 {
40+ s.area()
41+}
42+
43+// The bound is in a `where` clause, which is the same thing said elsewhere.
44+fn twice<S>(s: &S) -> u32
45+where
46+ S: Shape,
47+{
48+ s.scaled(2)
49+}
50+
51+fn main() {
52+ let sq = Square { side: 4 };
53+ let r = Rect { w: 3, h: 5 };
54+ println!("{} {}", describe(&sq), describe(&r));
55+ println!("{} {}", twice(&sq), twice(&r));
56+}