pub struct Stream<'s, S: Seed, A: Copy> {
next: Box<dyn Fn(S) -> Step<S, A> + 's>,
seed: S
}
Can someone summarize in a sentence or two? Or point to some docs? pub struct Stream<'s, S: Seed, A: Copy> {
next: Box<dyn Fn(S) -> Step<S, A> + 's>,
seed: S
}
Can someone summarize in a sentence or two? Or point to some docs?This chapter of the book is probably a good start:
it can be called with an S as the single argument and produces something of the type Step parametrized with S and A) and -does not- outlives the s lifetime.
's refers to a minimal lifetime and not a maximal limit.
That doesn't fully describe all of it, and there's some inferences there, but that's how I hear it in my head. Some of it is experience, and some of it is context, too. Especially that "an iterator" part. I'd expect to see Iterator when talking about iterators, but the 'shape' for a lack of a better word looks like one to me.
Rust's syntax is pretty regular and modular. After some practice, the bits start to fit together, and the way to tackle more complex signatures is the same as how you tackle complex code: break it up into smaller bits, and then assemble your understanding of the bits.
In Rust you work a lot with types. You can write code that supports several different types but you usually have to specify which constraints these types have to fulfill. You specify these constraints between <> brackets. In this example we have 3 such constraints:
1) <'s, S: Seed, A: Copy>
2 & 3) <dyn Fn(S) -> Step<S, A> + 's>
with 2) <S, A>
with 3) <dyn Fn(S) -> Step<S, A> + 's>
Because these constraints are all part of the same struct definiton, the names in these 3 constraints refer to the same things. The names are: s, S, A
Constraints in Rust can either be lifetimes or traits (similar to interfaces in Java). Lifetimes are marked with a single quote (') followed by the name of the lifetime (in this case: 's)
Let's look at constraint number 1):
I) We declare a lifetime 's but we do not specify where it will be applied in the struct yet.
II) We declare a type constraint S. Every type that is used as S must implement the Seed trait.
III) We declare a type constraint A. Every type that is used as A must implement the Copy trait.
Before I go into constraint 2&3, I must explain that `Fn(S) -> Step<S, A> + 's>` describes the type signature of a closure. The syntax is basically `Fn(parameter-list) -> return-value`.
Constraint 2) says that the return value must be a struct `Step` with the type parameters `S` and `A`.
Constraint 3) says that the next field of the struct must be a `Box` that contains an object of unknown size (hence the `dyn`) which is a closure that takes one `S` parameter and returns a `Step` struct with the type parameters `S` and `A`. The lifetime of the closure must be `'s`.
Finally, the seed must be of type `S`.
Rust is just great! I feel liberated from Java and Node.
But what intrigues me is why do programmers insist on making code look and feel difficult? Is there a secret obsession in good programmers to make their code look clever? Is variable naming not a basic etiquette?
I have gone back and forth on whether to align with the general convention on single-letter generic type names, but I’ve tended to come back to the single-letter approach any time I try making them more specific. The issue is of course that they are generic, and so naming them too explicitly robs them of some of their “this is any type” genericness. For lifetimes, I will definitely sometimes name them more explicitly than `’a`, but usually only when there’s more than one, as a way of being sure I don’t mix them up. However, the association of lifetimes with references tends to be straightforward enough that it often seems clearer and less messy to just name them with single characters.
We talk a lot about user experience and usability in our end products but we do not extend that thought to our fellow programmers at code level.