Earlier quoted context omitted.
Variance is an absolute disaster when it comes to language pedagogy. One of the smartest things Rust ever did was avoiding mentioning it in the surface-level syntax.
Why? It's just a property of type transformation. Assuming Parent - If Generic : it's covariant. - If Generic -> Generic : it's contravariant. - Otherwise: it's invariant. Or at least it's that straightforward in C#. Are there complications in Rust?
Under the hood: Vec
61–70 of 142 posts
Re: Under the hood: Vec<T>
#62Earlier quoted context omitted.
Suppose I think I may need 128 entries at some point, but the vector is allocated with room for 16 entries by default. I may not want to allocate and then immediately allocate again. But if I get to a 17th entry then I’m already causing allocation. So I might as well allocate 128 at that time so there are no more allocations at all.
All the functions mentioned above, even the cpp one, will reserve atleast the number of elements given to resize() or resize_exact(), but may reserve more than that. After some pondering, and reading the rust documentation, I came to the conclusion that te difference is this: reserve() will grow the underlaying memory area to the next increment, or more than one increment, while reserve_exact() will only grow the und…
The C++ one, however, will not reserve more than you ask for (in the case that you reserve greater than the current capacity). It's an exact reservation in the rust sense.
> reserve() will grow the underlaying memory area to the next increment, or more than one increment, while reserve_exact() will only grow the underlaying memory area to the next increment, but no more than that
No, not quite. Reserve will request as many increments as it needs, and reserve_exact will request the exact total capacity it needs.
Where the docs get confusing, is that the allocator also has a say here. In either case, if you ask for 21 items, and the allocator decides it prefers to give you a full page of memory that can contain, say, 32 items... then the Vec will use all the capacity returned by the allocator.
Re: Under the hood: Vec<T>
#63Earlier quoted context omitted.
Variance is an absolute disaster when it comes to language pedagogy. One of the smartest things Rust ever did was avoiding mentioning it in the surface-level syntax.
Why? It's just a property of type transformation. Assuming Parent - If Generic : it's covariant. - If Generic -> Generic : it's contravariant. - Otherwise: it's invariant. Or at least it's that straightforward in C#. Are there complications in Rust?
Scala was the first language in my exposure to try to simplify that away by lifting the variance annotations into the type parameter directly. It reduced some of the power but it made things easier to understand for developers. A full variance model would annotate specific features (methods) of a type as co/contra/invariant.
I'm not sure what approach C# takes - I haven't looked into it.
Rust doesn't expose variances for data structures at all. It exposes them for traits (type classes) and lifetimes, but neither of those are accessible in a higher order form. Trait usage is highly constrained and so is lifetime usage.
Traits mainly can be used as bounds on types. Some subset of traits, characterized as object traits, can be used as types themselves, but only in an indirect context. These are highly restricted. For example, if you have traits T and U where U extends T, and you have a reference `&dyn U`, you can't convert that to a `&dyn T` without knowledge of the underlying concrete type. You _can_ convert `&A where A: U` to `&B where B: T`, but that just falls out of the fact that the compiler has access to all the concrete type and trait definitions there and can validate the transform.
Rust's inheritance/variance story is a bit weird. They've kept it very minimal and constrained.
Re: Under the hood: Vec<T>
#64Because this is focused on how the data structure works it doesn't mention lots of nice API design choices in Rust. The one I particularly want to call out because it came up this morning is providing both Vec::reserve and Vec::reserve_exact Vec::reserve lets us hint about our upcoming capacity expectations without damaging the O(1) amortized growth which is the whole point of this collection type, but it can waste s…
> Vec::reserve_exact is a more narrow idea - we can hint about the ultimate capacity needed, if we're wrong and later need more capacity this has a significant performance cost because we thew away the amortized growth promise to get this, but we don't waste memory. The claim that using reserve_exact "throws away the amortized growth promise" is wrong. You don't disable amortized growth, you just won't get extra head…
The key part is that multiple calls reserve_exact will cause repeated allocations. The classic example is if someone defines a `pushfive` function that uses reserve_exact to increase the size by 5, and then pushes 5 times. Calling this function in a loop will take quadratic time since each reserve_exact call increases the array size. With reserve, the runtime is linear as expected.
Re: Under the hood: Vec<T>
#65Can anyone compare it to C++ in terms of memory layout for typical implementations?
Re: Under the hood: Vec<T>
#66Earlier quoted context omitted.
Suppose I think I may need 128 entries at some point, but the vector is allocated with room for 16 entries by default. I may not want to allocate and then immediately allocate again. But if I get to a 17th entry then I’m already causing allocation. So I might as well allocate 128 at that time so there are no more allocations at all.
All the functions mentioned above, even the cpp one, will reserve atleast the number of elements given to resize() or resize_exact(), but may reserve more than that. After some pondering, and reading the rust documentation, I came to the conclusion that te difference is this: reserve() will grow the underlaying memory area to the next increment, or more than one increment, while reserve_exact() will only grow the und…
Re: Under the hood: Vec<T>
#67I gave up half way through. The constant description of deep emotional feelings when it comes to something as sober as data types in the rust standard library make this rather hard to read. It is difficult to untangle the point the author is trying to make from the constant bombardment with deep emotional writing.
Re: Under the hood: Vec<T>
#68Earlier quoted context omitted.
All the functions mentioned above, even the cpp one, will reserve atleast the number of elements given to resize() or resize_exact(), but may reserve more than that. After some pondering, and reading the rust documentation, I came to the conclusion that te difference is this: reserve() will grow the underlaying memory area to the next increment, or more than one increment, while reserve_exact() will only grow the und…
> even the cpp one, will reserve atleast the number of elements given The C++ one, however, will not reserve more than you ask for (in the case that you reserve greater than the current capacity). It's an exact reservation in the rust sense. > reserve() will grow the underlaying memory area to the next increment, or more than one increment, while reserve_exact() will only grow the underlaying memory area to the next…
Of course, this can change in the future; in particular, the entire allocator API is still unstable and likely won't stabilize any time soon.
Re: Under the hood: Vec<T>
#69It is not clear, does compiler explicitly knows about properties of `NonNull` (for example, that it has neutral value to collapse `Option >`), so it is part of compiler, or it is expressed in type system, so it is part of standard library? Same question about other «signal» types.
compiler + standard library (core library to be more specific)
`NonNull` is mostly "just" a library type in the core libary, but it uses two unstable/nightly attributes to tell the compiler about it's special properties (see other answers, also to be clear its not a `lang_item` attribute but something other types can use, too).
The special thing about core/std in rust is that due to it being part of the specific compiler version it can use (some, careful considered) subset of unstable features as long as they work for that specific use case and don't "leak"/accidentally stabilize through it.
But this attributes are in progress of getting replaces with a new mechanism of pattern annotations. I.e. NonNull will be annotated with attribute which contains a `1..` pattern (i.e. value is 1 or larger) but other patterns are getting support, too. (The second attribute to enforce/guarantee niche optimizations might still be needed for stability guarantees.)
And I think??? there are plans to stabilize this new mechanism, but I'm absolutely not sure what the state of this is (RFC accepted?, stabilization open/likely/decided? implementation done/partial?). I'm currently not in the loop.
So in the long future you probably can write your own types with other niches e.g. a u8 with `..127` (MSB unused).
Re: Under the hood: Vec<T>
#70Earlier quoted context omitted.
I don't think the point of the article is the technical information, I think it's more of an emotional expression. Still valuable, just differently, I suppose.
Like much of what surrounds Rust. Looks quite emotional to me. If you do not know what I mean, go to the Rust reddit and discuss and compare on solid grounds without using an extremely flattering tone. You will see armies of fanatics voting negative.
The Rust community is doing a fantastic job of training both the next generation as well as those looking to dip their toes in.