Hi, use declarations are very convenient because they allow to clean up the code by opting-out from the path syntax.
However,
- In default associated function implementation in traits the following is not permitted:
fn innate_armor(level: u8) -> u8 {
use Self::{BASE_ARMOR, GROWTH};
BASE_ARMOR + GROWTH * level
}
e.g. in
struct Mage;
struct Swordsman;
struct Healer;
trait InnateArmor {
const BASE_ARMOR: u8;
const GROWTH: u8;
fn innate_armor(level: u8) -> u8 {
Self::BASE_ARMOR + Self::GROWTH * level
}
}
impl InnateArmor for Mage {
const BASE_ARMOR: u8 = 29;
const GROWTH: u8 = 2;
}
impl InnateArmor for Swordsman {
const BASE_ARMOR: u8 = 34;
const GROWTH: u8 = 3;
}
impl InnateArmor for Healer {
const BASE_ARMOR: u8 = 25;
const GROWTH: u8 = 3;
}
fn main() {
let (mia,sia,hia) = (
Mage::innate_armor(15),
Swordsman::innate_armor(15),
Healer::innate_armor(15)
);
println!(
"(29:34:25)->({}:{}:{})",
mia,
sia,
hia,
);
}
In such simple example, it's not too bad but I suppose that with more complex traits default implementations can be cleaned up by a lot.
EDIT: A person in the comment noted the case of using Default::default()
. There's a library that offers such a feature but I'm unsure whether it's a good idea to reach for an external library in such a simple case.
- Sometimes it's unclear which sort of item we (want to) import. With the pattern-designator-like syntax, we can provide better experience for readers of the code (especially, if there's IDE support, which may benefit the writers too due to better code suggestions).
use std::iter::{Empty,Once}:struct;
use std::borrow::{Cow}:enum;
use core::hash::{Hash}:trait;
// is it an enum, a struct, or a trait?
use my_crate::smth::Wtf;
// is it a function, a macro, or a module?
use another_crate::smth::parse;
The feature above can have some minor compile-time penalty, which may be justified by extra readability understandability of the code.
EDIT: even better syntax can be
use(enum) std::borrow::Cow;
use(trait) core::hash::Hash;
use(struct) std::iter::{Empty,Once};
which is similar to pub(crate)
and is considerably cleaner.
Follow up questions.
- Should
type
refer to a type alias or an enum/struct, or maybe enum/struct/type alias? (I think, the latter). - Will it be obvious for a reader coming from another language? (If it means an arbitrary type or type alias, I guess so).
- Would it be reasonable to reserve the
alias
keyword to make it obvious and support addition of trait aliases? (I think this is reasonable). - Should
alias
keyword be context dependent, i.e. be a keyword only in this context? (I'm not sure because it will be the first context-dependent keyword in the language, which means its addition comes with greater complexity of the language).
Maybe something like
use(type alias) syn::Result;
and maybe eventually
use(trait alias) nalgebra::Vector;
What are your thoughts on this?