Read a trait's associated constants through an accessor
`almost` compiles -- the fourth of libcosmic's dependencies to go the whole
way, and the first that needed traits to work.
A default body reads `Self::DEFAULT_TOLERANCE`, and which constant that is
depends on the instantiation. Nim has no associated constants on a generic, so
each impl also emits `proc rsTrait_CONST(_: typedesc[T]): Ty`, overloaded on
the type -- the same answer as for trait methods, and resolved at the same
point Rust resolves it.
The accessors are emitted after the types they name in a `typedesc`, and trait
default bodies after the accessors but before the procs that call them. That
is the seventh ordering constraint in this pass sequence; they are all of the
form "X names Y, so Y first".
Two things found on the way:
* Trait default bodies cannot be forward-declared with the rest. The
definition uses `auto` where the return type is a trait parameter, and a
forward saying anything else would not match it -- so they are emitted
before the bodies that call them instead.
* A `&'static str` constant was becoming `seq[char]`. `&str` is a borrowed
view of characters everywhere else, and `owned()` on a view gives a `seq`
-- but a constant has no buffer to borrow from, so it is a string.
Pre-existing, and unrelated to traits.
tests/cases/074.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>81195a6 parent: c9a5d39 modified
src/lower.rs +118 -3 | @@ -398,6 +398,13 @@ pub struct Lowerer { | ||
| 398 | 398 | /// Nim names for locals whose Rust name is shadowed. |
| 399 | 399 | shadow: Vec<HashMap<String, String>>, |
| 400 | 400 | shadow_n: usize, |
| 401 | + /// Associated constants a trait declares, by `(trait, name)` -> type. | |
| 402 | + /// A default body reads them through `Self`, where there is no concrete | |
| 403 | + /// type to name. | |
| 404 | + trait_consts: HashMap<(String, String), Nim>, | |
| 405 | + /// Accessors for associated constants, emitted after the types they | |
| 406 | + /// name in a `typedesc`. | |
| 407 | + const_accessors: Vec<String>, | |
| 401 | 408 | /// Procs that cannot be translated, by Nim name, with the reason. |
| 402 | 409 | /// |
| 403 | 410 | /// Not an error on its own. A dependency graph carries far more code than |
| @@ -528,6 +535,8 @@ impl Lowerer { | ||
| 528 | 535 | cur_sig_generics: std::cell::RefCell::new(Vec::new()), |
| 529 | 536 | shadow: vec![HashMap::new()], |
| 530 | 537 | shadow_n: 0, |
| 538 | + trait_consts: HashMap::new(), | |
| 539 | + const_accessors: Vec::new(), | |
| 531 | 540 | untranslatable: HashMap::new(), |
| 532 | 541 | enum_module: HashMap::new(), |
| 533 | 542 | report: false, |
| @@ -791,6 +800,27 @@ impl Lowerer { | ||
| 791 | 800 | } |
| 792 | 801 | } |
| 793 | 802 | |
| 803 | + // The associated-constant accessors, now that their types exist. | |
| 804 | + if !self.const_accessors.is_empty() { | |
| 805 | + for line in std::mem::take(&mut self.const_accessors) { | |
| 806 | + self.line(&line); | |
| 807 | + } | |
| 808 | + self.blank(); | |
| 809 | + } | |
| 810 | + | |
| 811 | + // Pass 3.7: trait default bodies. They are generic procs and cannot be | |
| 812 | + // forward-declared alongside the rest -- the definition uses `auto` | |
| 813 | + // where the return type is a trait parameter, and a forward saying | |
| 814 | + // anything else would not match it. Emitted before the bodies that | |
| 815 | + // call them instead. | |
| 816 | + for (m, f) in files { | |
| 817 | + self.cur_mod = m.clone(); | |
| 818 | + for item in &f.items { | |
| 819 | + let r = self.emit_trait_defaults(item); | |
| 820 | + self.note(r)?; | |
| 821 | + } | |
| 822 | + } | |
| 823 | + | |
| 794 | 824 | self.body_start = self.out.len(); |
| 795 | 825 | |
| 796 | 826 | // Pass 4: bodies. |
| @@ -1086,6 +1116,12 @@ impl Lowerer { | ||
| 1086 | 1116 | let outer_self = |
| 1087 | 1117 | self.self_ty.replace(Nim::Named("Self".into(), Vec::new())); |
| 1088 | 1118 | for it in &t.items { |
| 1119 | + if let syn::TraitItem::Const(c) = it { | |
| 1120 | + if let Ok(ty) = self.map_ty(&c.ty) { | |
| 1121 | + self.trait_consts | |
| 1122 | + .insert((tname.clone(), c.ident.to_string()), ty.owned()); | |
| 1123 | + } | |
| 1124 | + } | |
| 1089 | 1125 | if let syn::TraitItem::Fn(f) = it { |
| 1090 | 1126 | self.note_bounds(&f.sig.generics); |
| 1091 | 1127 | if let Ok((mut params, ret)) = self.signature(&f.sig) { |
| @@ -2847,6 +2883,33 @@ impl Lowerer { | ||
| 2847 | 2883 | // --------------------------------------------------------------- items |
| 2848 | 2884 | |
| 2849 | 2885 | /// Emit the type definitions only: they must precede every signature. |
| 2886 | + /// Write out a trait's default bodies, ahead of the procs that call them. | |
| 2887 | + fn emit_trait_defaults(&mut self, item: &Item) -> Result<(), String> { | |
| 2888 | + if !self.cfg_keeps(item_attrs(item))? { | |
| 2889 | + return Ok(()); | |
| 2890 | + } | |
| 2891 | + match item { | |
| 2892 | + Item::Trait(_) => self.item_inner(item), | |
| 2893 | + Item::Mod(m) => { | |
| 2894 | + if let Some((_, items)) = &m.content { | |
| 2895 | + for i in items { | |
| 2896 | + self.emit_trait_defaults(i)?; | |
| 2897 | + } | |
| 2898 | + } | |
| 2899 | + Ok(()) | |
| 2900 | + } | |
| 2901 | + Item::Const(c) => { | |
| 2902 | + if let Some(items) = derive_item_block(c) { | |
| 2903 | + for i in &items { | |
| 2904 | + self.emit_trait_defaults(i)?; | |
| 2905 | + } | |
| 2906 | + } | |
| 2907 | + Ok(()) | |
| 2908 | + } | |
| 2909 | + _ => Ok(()), | |
| 2910 | + } | |
| 2911 | + } | |
| 2912 | + | |
| 2850 | 2913 | /// Write out the module-level constants, ahead of everything that can |
| 2851 | 2914 | /// name one. |
| 2852 | 2915 | fn emit_consts(&mut self, item: &Item) -> Result<(), String> { |
| @@ -3017,10 +3080,14 @@ impl Lowerer { | ||
| 3017 | 3080 | return Ok(()); |
| 3018 | 3081 | } |
| 3019 | 3082 | } |
| 3020 | - // Types and statics were emitted in their own passes. | |
| 3083 | + // Types, statics and trait defaults were emitted in their own passes. | |
| 3021 | 3084 | if matches!( |
| 3022 | 3085 | item, |
| 3023 | - Item::Struct(_) | Item::Enum(_) | Item::Const(_) | Item::Static(_) | |
| 3086 | + Item::Struct(_) | |
| 3087 | + | Item::Enum(_) | |
| 3088 | + | Item::Const(_) | |
| 3089 | + | Item::Static(_) | |
| 3090 | + | Item::Trait(_) | |
| 3024 | 3091 | ) { |
| 3025 | 3092 | return Ok(()); |
| 3026 | 3093 | } |
| @@ -3411,10 +3478,37 @@ impl Lowerer { | ||
| 3411 | 3478 | { |
| 3412 | 3479 | return Ok(()); |
| 3413 | 3480 | } |
| 3414 | - let t = self.map_ty(&c.ty)?.owned(); | |
| 3481 | + // A `&'static str` constant is an owned string, not a borrowed view of | |
| 3482 | + // one: there is no buffer for it to borrow from, and `owned()` on a | |
| 3483 | + // view of characters gives `seq[char]`, which is not what a string | |
| 3484 | + // literal is. | |
| 3485 | + let t = match self.map_ty(&c.ty)? { | |
| 3486 | + Nim::OpenArray(e) if matches!(&*e, Nim::Prim(p) if p == "char") => { | |
| 3487 | + Nim::Prim("string".into()) | |
| 3488 | + } | |
| 3489 | + other => other.owned(), | |
| 3490 | + }; | |
| 3415 | 3491 | let v = self.expr_at(&c.expr, Some(&t))?; |
| 3416 | 3492 | let name = format!("{}_{}", tyname, c.ident); |
| 3417 | 3493 | self.line(&format!("const {}*: {} = {}", ident(&name), t.render(), v.code)); |
| 3494 | + // A trait's default body reads this through `Self`, where there is no | |
| 3495 | + // concrete type to name. An accessor overloaded on the type is how | |
| 3496 | + // Nim answers that, the same way it answers a trait method. | |
| 3497 | + // Deferred: it names the type in a `typedesc`, which has to be | |
| 3498 | + // declared first, and constants are written before the types are. | |
| 3499 | + let trs = self.type_traits.get(tyname).cloned().unwrap_or_default(); | |
| 3500 | + for (tr, _) in trs { | |
| 3501 | + if self.trait_consts.contains_key(&(tr.clone(), c.ident.to_string())) { | |
| 3502 | + self.const_accessors.push(format!( | |
| 3503 | + "proc rs{}_{}*(_: typedesc[{}]): {} = {}", | |
| 3504 | + tr, | |
| 3505 | + c.ident, | |
| 3506 | + ident(tyname), | |
| 3507 | + t.render(), | |
| 3508 | + ident(&name) | |
| 3509 | + )); | |
| 3510 | + } | |
| 3511 | + } | |
| 3418 | 3512 | self.blank(); |
| 3419 | 3513 | self.assoc_consts |
| 3420 | 3514 | .insert((tyname.to_string(), c.ident.to_string()), (ident(&name), t)); |
| @@ -6176,6 +6270,27 @@ impl Lowerer { | ||
| 6176 | 6270 | } |
| 6177 | 6271 | } |
| 6178 | 6272 | |
| 6273 | + // `Self::CONST` where `Self` is a type parameter: which | |
| 6274 | + // constant that is depends on the instantiation, so it is read | |
| 6275 | + // through the accessor rather than named. | |
| 6276 | + if let Some(q) = p.path.segments.iter().rev().nth(1) { | |
| 6277 | + if q.ident == "Self" | |
| 6278 | + && self.self_ty.as_ref().map(type_name).as_deref() == Some("Self") | |
| 6279 | + { | |
| 6280 | + let hit = self | |
| 6281 | + .trait_consts | |
| 6282 | + .iter() | |
| 6283 | + .find(|((_, n), _)| *n == name) | |
| 6284 | + .map(|((tr, _), ty)| (tr.clone(), ty.clone())); | |
| 6285 | + if let Some((tr, ty)) = hit { | |
| 6286 | + return Ok(Val::new( | |
| 6287 | + format!("rs{}_{}(Self)", tr, name), | |
| 6288 | + Some(ty), | |
| 6289 | + )); | |
| 6290 | + } | |
| 6291 | + } | |
| 6292 | + } | |
| 6293 | + | |
| 6179 | 6294 | // `i32::MAX` and friends: an associated const on a primitive. |
| 6180 | 6295 | // The float constants, written out. Nim has its own spellings |
| 6181 | 6296 | // for some and none for others, and these are the values Rust |
| @@ -398,6 +398,13 @@ pub struct Lowerer { | |||
| 398 | /// Nim names for locals whose Rust name is shadowed. | 398 | /// Nim names for locals whose Rust name is shadowed. |
| 399 | shadow: Vec<HashMap<String, String>>, | 399 | shadow: Vec<HashMap<String, String>>, |
| 400 | shadow_n: usize, | 400 | shadow_n: usize, |
| 401 | + /// Associated constants a trait declares, by `(trait, name)` -> type. | ||
| 402 | + /// A default body reads them through `Self`, where there is no concrete | ||
| 403 | + /// type to name. | ||
| 404 | + trait_consts: HashMap<(String, String), Nim>, | ||
| 405 | + /// Accessors for associated constants, emitted after the types they | ||
| 406 | + /// name in a `typedesc`. | ||
| 407 | + const_accessors: Vec<String>, | ||
| 401 | /// Procs that cannot be translated, by Nim name, with the reason. | 408 | /// Procs that cannot be translated, by Nim name, with the reason. |
| 402 | /// | 409 | /// |
| 403 | /// Not an error on its own. A dependency graph carries far more code than | 410 | /// Not an error on its own. A dependency graph carries far more code than |
| @@ -528,6 +535,8 @@ impl Lowerer { | |||
| 528 | cur_sig_generics: std::cell::RefCell::new(Vec::new()), | 535 | cur_sig_generics: std::cell::RefCell::new(Vec::new()), |
| 529 | shadow: vec![HashMap::new()], | 536 | shadow: vec![HashMap::new()], |
| 530 | shadow_n: 0, | 537 | shadow_n: 0, |
| 538 | + trait_consts: HashMap::new(), | ||
| 539 | + const_accessors: Vec::new(), | ||
| 531 | untranslatable: HashMap::new(), | 540 | untranslatable: HashMap::new(), |
| 532 | enum_module: HashMap::new(), | 541 | enum_module: HashMap::new(), |
| 533 | report: false, | 542 | report: false, |
| @@ -791,6 +800,27 @@ impl Lowerer { | |||
| 791 | } | 800 | } |
| 792 | } | 801 | } |
| 793 | 802 | ||
| 803 | + // The associated-constant accessors, now that their types exist. | ||
| 804 | + if !self.const_accessors.is_empty() { | ||
| 805 | + for line in std::mem::take(&mut self.const_accessors) { | ||
| 806 | + self.line(&line); | ||
| 807 | + } | ||
| 808 | + self.blank(); | ||
| 809 | + } | ||
| 810 | + | ||
| 811 | + // Pass 3.7: trait default bodies. They are generic procs and cannot be | ||
| 812 | + // forward-declared alongside the rest -- the definition uses `auto` | ||
| 813 | + // where the return type is a trait parameter, and a forward saying | ||
| 814 | + // anything else would not match it. Emitted before the bodies that | ||
| 815 | + // call them instead. | ||
| 816 | + for (m, f) in files { | ||
| 817 | + self.cur_mod = m.clone(); | ||
| 818 | + for item in &f.items { | ||
| 819 | + let r = self.emit_trait_defaults(item); | ||
| 820 | + self.note(r)?; | ||
| 821 | + } | ||
| 822 | + } | ||
| 823 | + | ||
| 794 | self.body_start = self.out.len(); | 824 | self.body_start = self.out.len(); |
| 795 | 825 | ||
| 796 | // Pass 4: bodies. | 826 | // Pass 4: bodies. |
| @@ -1086,6 +1116,12 @@ impl Lowerer { | |||
| 1086 | let outer_self = | 1116 | let outer_self = |
| 1087 | self.self_ty.replace(Nim::Named("Self".into(), Vec::new())); | 1117 | self.self_ty.replace(Nim::Named("Self".into(), Vec::new())); |
| 1088 | for it in &t.items { | 1118 | for it in &t.items { |
| 1119 | + if let syn::TraitItem::Const(c) = it { | ||
| 1120 | + if let Ok(ty) = self.map_ty(&c.ty) { | ||
| 1121 | + self.trait_consts | ||
| 1122 | + .insert((tname.clone(), c.ident.to_string()), ty.owned()); | ||
| 1123 | + } | ||
| 1124 | + } | ||
| 1089 | if let syn::TraitItem::Fn(f) = it { | 1125 | if let syn::TraitItem::Fn(f) = it { |
| 1090 | self.note_bounds(&f.sig.generics); | 1126 | self.note_bounds(&f.sig.generics); |
| 1091 | if let Ok((mut params, ret)) = self.signature(&f.sig) { | 1127 | if let Ok((mut params, ret)) = self.signature(&f.sig) { |
| @@ -2847,6 +2883,33 @@ impl Lowerer { | |||
| 2847 | // --------------------------------------------------------------- items | 2883 | // --------------------------------------------------------------- items |
| 2848 | 2884 | ||
| 2849 | /// Emit the type definitions only: they must precede every signature. | 2885 | /// Emit the type definitions only: they must precede every signature. |
| 2886 | + /// Write out a trait's default bodies, ahead of the procs that call them. | ||
| 2887 | + fn emit_trait_defaults(&mut self, item: &Item) -> Result<(), String> { | ||
| 2888 | + if !self.cfg_keeps(item_attrs(item))? { | ||
| 2889 | + return Ok(()); | ||
| 2890 | + } | ||
| 2891 | + match item { | ||
| 2892 | + Item::Trait(_) => self.item_inner(item), | ||
| 2893 | + Item::Mod(m) => { | ||
| 2894 | + if let Some((_, items)) = &m.content { | ||
| 2895 | + for i in items { | ||
| 2896 | + self.emit_trait_defaults(i)?; | ||
| 2897 | + } | ||
| 2898 | + } | ||
| 2899 | + Ok(()) | ||
| 2900 | + } | ||
| 2901 | + Item::Const(c) => { | ||
| 2902 | + if let Some(items) = derive_item_block(c) { | ||
| 2903 | + for i in &items { | ||
| 2904 | + self.emit_trait_defaults(i)?; | ||
| 2905 | + } | ||
| 2906 | + } | ||
| 2907 | + Ok(()) | ||
| 2908 | + } | ||
| 2909 | + _ => Ok(()), | ||
| 2910 | + } | ||
| 2911 | + } | ||
| 2912 | + | ||
| 2850 | /// Write out the module-level constants, ahead of everything that can | 2913 | /// Write out the module-level constants, ahead of everything that can |
| 2851 | /// name one. | 2914 | /// name one. |
| 2852 | fn emit_consts(&mut self, item: &Item) -> Result<(), String> { | 2915 | fn emit_consts(&mut self, item: &Item) -> Result<(), String> { |
| @@ -3017,10 +3080,14 @@ impl Lowerer { | |||
| 3017 | return Ok(()); | 3080 | return Ok(()); |
| 3018 | } | 3081 | } |
| 3019 | } | 3082 | } |
| 3020 | - // Types and statics were emitted in their own passes. | 3083 | + // Types, statics and trait defaults were emitted in their own passes. |
| 3021 | if matches!( | 3084 | if matches!( |
| 3022 | item, | 3085 | item, |
| 3023 | - Item::Struct(_) | Item::Enum(_) | Item::Const(_) | Item::Static(_) | 3086 | + Item::Struct(_) |
| 3087 | + | Item::Enum(_) | ||
| 3088 | + | Item::Const(_) | ||
| 3089 | + | Item::Static(_) | ||
| 3090 | + | Item::Trait(_) | ||
| 3024 | ) { | 3091 | ) { |
| 3025 | return Ok(()); | 3092 | return Ok(()); |
| 3026 | } | 3093 | } |
| @@ -3411,10 +3478,37 @@ impl Lowerer { | |||
| 3411 | { | 3478 | { |
| 3412 | return Ok(()); | 3479 | return Ok(()); |
| 3413 | } | 3480 | } |
| 3414 | - let t = self.map_ty(&c.ty)?.owned(); | 3481 | + // A `&'static str` constant is an owned string, not a borrowed view of |
| 3482 | + // one: there is no buffer for it to borrow from, and `owned()` on a | ||
| 3483 | + // view of characters gives `seq[char]`, which is not what a string | ||
| 3484 | + // literal is. | ||
| 3485 | + let t = match self.map_ty(&c.ty)? { | ||
| 3486 | + Nim::OpenArray(e) if matches!(&*e, Nim::Prim(p) if p == "char") => { | ||
| 3487 | + Nim::Prim("string".into()) | ||
| 3488 | + } | ||
| 3489 | + other => other.owned(), | ||
| 3490 | + }; | ||
| 3415 | let v = self.expr_at(&c.expr, Some(&t))?; | 3491 | let v = self.expr_at(&c.expr, Some(&t))?; |
| 3416 | let name = format!("{}_{}", tyname, c.ident); | 3492 | let name = format!("{}_{}", tyname, c.ident); |
| 3417 | self.line(&format!("const {}*: {} = {}", ident(&name), t.render(), v.code)); | 3493 | self.line(&format!("const {}*: {} = {}", ident(&name), t.render(), v.code)); |
| 3494 | + // A trait's default body reads this through `Self`, where there is no | ||
| 3495 | + // concrete type to name. An accessor overloaded on the type is how | ||
| 3496 | + // Nim answers that, the same way it answers a trait method. | ||
| 3497 | + // Deferred: it names the type in a `typedesc`, which has to be | ||
| 3498 | + // declared first, and constants are written before the types are. | ||
| 3499 | + let trs = self.type_traits.get(tyname).cloned().unwrap_or_default(); | ||
| 3500 | + for (tr, _) in trs { | ||
| 3501 | + if self.trait_consts.contains_key(&(tr.clone(), c.ident.to_string())) { | ||
| 3502 | + self.const_accessors.push(format!( | ||
| 3503 | + "proc rs{}_{}*(_: typedesc[{}]): {} = {}", | ||
| 3504 | + tr, | ||
| 3505 | + c.ident, | ||
| 3506 | + ident(tyname), | ||
| 3507 | + t.render(), | ||
| 3508 | + ident(&name) | ||
| 3509 | + )); | ||
| 3510 | + } | ||
| 3511 | + } | ||
| 3418 | self.blank(); | 3512 | self.blank(); |
| 3419 | self.assoc_consts | 3513 | self.assoc_consts |
| 3420 | .insert((tyname.to_string(), c.ident.to_string()), (ident(&name), t)); | 3514 | .insert((tyname.to_string(), c.ident.to_string()), (ident(&name), t)); |
| @@ -6176,6 +6270,27 @@ impl Lowerer { | |||
| 6176 | } | 6270 | } |
| 6177 | } | 6271 | } |
| 6178 | 6272 | ||
| 6273 | + // `Self::CONST` where `Self` is a type parameter: which | ||
| 6274 | + // constant that is depends on the instantiation, so it is read | ||
| 6275 | + // through the accessor rather than named. | ||
| 6276 | + if let Some(q) = p.path.segments.iter().rev().nth(1) { | ||
| 6277 | + if q.ident == "Self" | ||
| 6278 | + && self.self_ty.as_ref().map(type_name).as_deref() == Some("Self") | ||
| 6279 | + { | ||
| 6280 | + let hit = self | ||
| 6281 | + .trait_consts | ||
| 6282 | + .iter() | ||
| 6283 | + .find(|((_, n), _)| *n == name) | ||
| 6284 | + .map(|((tr, _), ty)| (tr.clone(), ty.clone())); | ||
| 6285 | + if let Some((tr, ty)) = hit { | ||
| 6286 | + return Ok(Val::new( | ||
| 6287 | + format!("rs{}_{}(Self)", tr, name), | ||
| 6288 | + Some(ty), | ||
| 6289 | + )); | ||
| 6290 | + } | ||
| 6291 | + } | ||
| 6292 | + } | ||
| 6293 | + | ||
| 6179 | // `i32::MAX` and friends: an associated const on a primitive. | 6294 | // `i32::MAX` and friends: an associated const on a primitive. |
| 6180 | // The float constants, written out. Nim has its own spellings | 6295 | // The float constants, written out. Nim has its own spellings |
| 6181 | // for some and none for others, and these are the values Rust | 6296 | // for some and none for others, and these are the values Rust |
added
tests/cases/074-trait-assoc-const.rs +37 -0 | new file mode 100644 | ||
| @@ -0,0 +1,37 @@ | ||
| 1 | +// A trait's associated constant, read from a default body through `Self`. | |
| 2 | +// Which constant that is depends on the instantiation, so it goes through an | |
| 3 | +// accessor overloaded on the type — the same way a trait method does. | |
| 4 | +trait Limits { | |
| 5 | + const CEILING: u32; | |
| 6 | + const NAME: &'static str; | |
| 7 | + | |
| 8 | + fn clamp(&self, v: u32) -> u32 { | |
| 9 | + if v > Self::CEILING { Self::CEILING } else { v } | |
| 10 | + } | |
| 11 | + | |
| 12 | + fn label(&self) -> String { | |
| 13 | + format!("{} <= {}", Self::NAME, Self::CEILING) | |
| 14 | + } | |
| 15 | +} | |
| 16 | + | |
| 17 | +struct Small; | |
| 18 | +struct Large; | |
| 19 | + | |
| 20 | +impl Limits for Small { | |
| 21 | + const CEILING: u32 = 10; | |
| 22 | + const NAME: &'static str = "small"; | |
| 23 | +} | |
| 24 | + | |
| 25 | +impl Limits for Large { | |
| 26 | + const CEILING: u32 = 1000; | |
| 27 | + const NAME: &'static str = "large"; | |
| 28 | +} | |
| 29 | + | |
| 30 | +fn main() { | |
| 31 | + let s = Small; | |
| 32 | + let l = Large; | |
| 33 | + println!("{} {}", s.clamp(5), s.clamp(50)); | |
| 34 | + println!("{} {}", l.clamp(5), l.clamp(5000)); | |
| 35 | + println!("{}", s.label()); | |
| 36 | + println!("{}", l.label()); | |
| 37 | +} | |
| new file mode 100644 | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | +// A trait's associated constant, read from a default body through `Self`. | ||
| 2 | +// Which constant that is depends on the instantiation, so it goes through an | ||
| 3 | +// accessor overloaded on the type — the same way a trait method does. | ||
| 4 | +trait Limits { | ||
| 5 | + const CEILING: u32; | ||
| 6 | + const NAME: &'static str; | ||
| 7 | + | ||
| 8 | + fn clamp(&self, v: u32) -> u32 { | ||
| 9 | + if v > Self::CEILING { Self::CEILING } else { v } | ||
| 10 | + } | ||
| 11 | + | ||
| 12 | + fn label(&self) -> String { | ||
| 13 | + format!("{} <= {}", Self::NAME, Self::CEILING) | ||
| 14 | + } | ||
| 15 | +} | ||
| 16 | + | ||
| 17 | +struct Small; | ||
| 18 | +struct Large; | ||
| 19 | + | ||
| 20 | +impl Limits for Small { | ||
| 21 | + const CEILING: u32 = 10; | ||
| 22 | + const NAME: &'static str = "small"; | ||
| 23 | +} | ||
| 24 | + | ||
| 25 | +impl Limits for Large { | ||
| 26 | + const CEILING: u32 = 1000; | ||
| 27 | + const NAME: &'static str = "large"; | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +fn main() { | ||
| 31 | + let s = Small; | ||
| 32 | + let l = Large; | ||
| 33 | + println!("{} {}", s.clamp(5), s.clamp(50)); | ||
| 34 | + println!("{} {}", l.clamp(5), l.clamp(5000)); | ||
| 35 | + println!("{}", s.label()); | ||
| 36 | + println!("{}", l.label()); | ||
| 37 | +} | ||