"Generic parameters" broadly refers to both generic type parameters and generic lifetime parameters.
Nowadays there’s also const generics. E.g.
trait Foo<const N: usize> {}
Regarding generics and traits, there might be value in trying to reproduce some of the gist of (my posts in) this thread – at least OP found it helpful there, this also plays into viewing traits with type arguments as a way to do multiple dispatch.
Depending on how long the “traits basics” is supposed to be you might want to mention orphan instances, too… oh wait
The rules Rust uses to enforce trait coherence, the implications of those rules, and workarounds for the implications are outside the scope of this article.
maybe you at least want to mention the term “orphan impl” or “orphan rules” or link to something like the reference and/or this post (which has more interesting links). Also, since you explain blanket impls, you might want to mention that adding blanket impls can result in semver-breaking changes while adding ordinary impls is usually not (considered) a breaking change.
Your object safety definition seems off, in particular
A trait method is object-safe if it meets these requirements:
- method requires
Self: Sized
or
- method does not use
Self
anywhere other than the first argument
a method without an actual self
parameter/receiver isn’t object safe either, e.g.
trait Foo {
fn foo(this: Self);
}
impl dyn Foo {}
2 | trait Foo {
| --- this trait cannot be made into an object...
3 | fn foo(this: Self);
| ^^^ ...because associated function `foo` has no `self` parameter
you might want to copy/adapt the definition from the reference more closely.
I don’t really love the term “impl’d”, I think “implemented” might be nicer. It also corresponds to how rustc always speaks of “implement” or “implementation”.
Your prelude listing doesn’t contain IntoIterator
(and maybe some more are missing). You should double-check the list / recreate it from the docs.
Almost all types are Send
and Sync
. The only notable Send
exception is Rc
and the only notable Sync
exceptions are Rc
, Cell
, RefCell
. If you need a Send
version of Rc
use Arc
. If you need a Sync
version of Cell
or RefCell
use Mutex
or RwLock
.
Maybe mention raw pointers not being send/sync and atomic types as an alternative for Cell
(when applicable).
Edit: Another point
Functions
A trait function is any function whose first argument is not of type Self
or some type which dereferences to Self
, e.g. &Self
, &mut Self
, Box<Self>
.
Methods
A trait method is any function whose first argument is of type Self
or some type which dereferences to Self
, e.g. &Self
, &mut Self
, Box<Self>
.
Methods can be called using the dot operator on the implementing type:
That’s inaccurate. The relevant difference is whether there’s a (first) parameter called “self
” (which is a keyword and can only be used for the first argument when the type is a valid receiver type). Also, valid receiver types (according to the reference) are only:
If the type of the self
parameter is specified, it is limited to types resolving to one generated by the following grammar (where 'lt
denotes some arbitrary lifetime):
P = &'lt S | &'lt mut S | Box<S> | Rc<S> | Arc<S> | Pin<P>
S = Self | P
The Self
terminal in this grammar denotes a type resolving to the implementing type. This can also include the contextual type alias Self
, other type aliases, or associated type projections resolving to the implementing type.
Also you define a “function”/“trait function” (which might IMO perhaps better be called an “associated function” (effectively) as a function that is not a method. Then you say
However methods are just functions with syntax sugar
It’s all a bit confusing; in my experience there is no real terminology for an associated funtion that is not a method. Also compare with the reference on these definitions/terminology.
Edit2:
However, the idiomatic refactor is actually:
fn example<I: Iterator<Item = i32>>(mut iter: I) {
let first3: Vec<_> = (&mut iter).take(3).collect();
for item in iter { // âś…
// process remaining items
}
}
I’d actually argue in favor of iter.by_ref().take(3).collect()
. Also – even though it is useful to know that underscores can be used for this – I think that an explicit Vec<i32>
annotation might be more clear in demonstrating that even though &mut
or by_ref()
is involved, the Item
type stays the same.
Does that not include e.g. DoubleEndedIterator
? It’s even in the prelude.