<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.3">Jekyll</generator><link href="http://orodu.net/feed.xml" rel="self" type="application/atom+xml" /><link href="http://orodu.net/" rel="alternate" type="text/html" /><updated>2023-12-27T22:25:10+00:00</updated><id>http://orodu.net/feed.xml</id><title type="html">An Update on Writing Memory Safety Bugs</title><subtitle>How could we write better software, and write software better?</subtitle><author><name>Dana Jansens</name></author><entry><title type="html">How to Build Abstractions in Rust Applications: The Missing Rung on the Rust Education Ladder</title><link href="http://orodu.net/2023/12/27/rust-abstractions.html" rel="alternate" type="text/html" title="How to Build Abstractions in Rust Applications: The Missing Rung on the Rust Education Ladder" /><published>2023-12-27T00:00:00+00:00</published><updated>2023-12-27T00:00:00+00:00</updated><id>http://orodu.net/2023/12/27/rust-abstractions</id><content type="html" xml:base="http://orodu.net/2023/12/27/rust-abstractions.html">&lt;p&gt;I read yet another reddit post where an OO developer struggles with building abstractions in Rust. These things feel familiar to me, I have had the same struggles moving from being a C++ developer to building a non-trivial Rust application and trying to use abstractions throughout to reduce coupling. And I see colleagues at work ask the same things as they start to pick up Rust.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/rust/comments/18rme0v/interface_abstractions_in_rust/&quot;&gt;https://www.reddit.com/r/rust/comments/18rme0v/interface_abstractions_in_rust/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To me, the most helpful reply in here was a reframing or changing of perspective. Instead of building a storage system, which is behaviour and state wrapped up in a typed object, build a function that needs to use a storage system and provide the &lt;em&gt;functions&lt;/em&gt; it needs through a generic trait.&lt;/p&gt;

&lt;p&gt;Another way of saying this could be to move the abstraction out of the incoming object (a virtual object) and into the function signature (a generic function). But I feel like it does not really explain &lt;em&gt;why&lt;/em&gt; that is better, though it is more idiomatic. In fact, you could reword what I just said as “use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;impl T&lt;/code&gt; instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&amp;lt;dyn T&amp;gt;&lt;/code&gt;” but that seems to miss the actual point that is hiding inside this advice.&lt;/p&gt;

&lt;p&gt;Another way to think about it could be in terms of design direction. Write the code that needs the trait first, then define the trait in terms of what was needed, and provide the impl. But what if you have two teams, and you want one to go build the storage system for the other team? You don’t want to block on them building their application on top of some no-op storage system which you will then later use to design an API with. You want the storage system team to go write an API and provide it as a service. How it’s consumed, by generic or trait object, is not ineherently tied to the order in which they are developed, but it has big implications for how the abstraction is used and built. And since working with concrete types is very different from working with trait objects, starting from concrete types can lead to a better outcome without explicitly stating the underlying intentions.&lt;/p&gt;

&lt;p&gt;There’s an intense need for OO developers to build a typed object that provides functionality. That is a basic part of the recipe for abstraction C++ and Java. So it’s extremely natural for devs to end up at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&amp;lt;dyn T&amp;gt;&lt;/code&gt;, which provides for this need but does so very poorly and in a way that ends up feeling like you’re fighting the Rust language.&lt;/p&gt;

&lt;p&gt;Trait objects can be useful for small independent implementations with a narrow scope (like an iterator, or a closure), but as a tool for building application abstractions they impose a ton of pain on both the implementor of the abstraction and the consumer. For the implementor, they end up building &lt;em&gt;everything&lt;/em&gt; into this one trait. Even &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Clone&lt;/code&gt;, as we see mentioned in the above Reddit discussion. Downcasting is another example which you have to roll yourself through your trait API. Then the consumer of your trait can’t make use of standard vocabulary and traits, and everything becomes quite full of friction and complexity.&lt;/p&gt;

&lt;p&gt;Since trait objects APIs become monolithic, they lead developers toward composing traits together through “inheriting”. This is a totally normal thing to do in C++, and the syntax even looks the same, but combining traits in this way to build godlike trait objects does not lead to happy times. There is lots of documentation around explaining that trait inheritance is different in Rust and that you should not use it in this way, but less on how to build abstractions for your application correctly in spite of this.&lt;/p&gt;

&lt;p&gt;Restating the given advice in another way, it says to use “generic objects which provide a trait impl” instead of “trait objects”. Then there is no need to make the traits object-safe at all, which is great because requiring it could get in the way of providing the ideal interface, such as preventing the use of generics within the interface. Not that you’d put generics in a C++ abstraction, since you can’t combine generics and virtual in C++, so this doesn’t seem like a problem worth worrying about to the new Rust developer.&lt;/p&gt;

&lt;p&gt;It &lt;em&gt;is&lt;/em&gt; helpful advice to Rust devs that functions should receive abstractions as generics instead of as trait objects. But it’s missing the underlying intention of &lt;em&gt;why&lt;/em&gt; a trait object seemed like the better fit. There’s two main reasons that come to mind:&lt;/p&gt;

&lt;h2 id=&quot;testing&quot;&gt;Testing&lt;/h2&gt;

&lt;p&gt;It is standard practice in large C++ codebases, take Chromium and Blink for example, to build virtual abstractions around pieces of production code and then replace the implementation with something else when running tests. This is especially true for integration tests, but it is a mechanism used exhaustively (almost certainly over-used) for unit tests as well. I don’t think advice that handwaves at how generics make mocking harder are doing service to this intent.&lt;/p&gt;

&lt;p&gt;Blink’s WebTest harness is a good example of this type of system. The production code provides all kind of functionality to Blink, but in tests various things are stubbed out and replaced in order to control inputs that would normally be coming from devices, or the user, or the network. This is foundational for being able to reliably test the Blink implementation of the web platform.&lt;/p&gt;

&lt;p&gt;With generics, it is still possible to write tests for functions which make use of an abstraction in isolation, but you can no longer write a factory that returns an implementation of some abstract behaviour, and replace the whole implementation in tests. So writing integration tests of a large system appears impossible without trait objects, unless… the &lt;em&gt;entire codebase&lt;/em&gt; is parameterized over a set of generic traits. There may be a single production implementation of all those traits, maybe even a single type/object which implements all those traits. Teams can freely write traits and implementations for those traits in their own modules. And when building for tests, a different type may be selected &lt;em&gt;at compile time&lt;/em&gt;, which comes with different implementations.&lt;/p&gt;

&lt;p&gt;If this should be a blessed design pattern, Rust education needs to lean into it when teaching developers about abstractions in Rust. Current education seems heavily skewed toward authoring libraries. And while you can think of an application as a collection of libraries, it is also meaningfully different in how it all comes together as one huge unit, and how concepts may have to span across many parts of a codebase, and be implemented by many teams. It should be expected that a large application comes with a need to provide abstractions that differ in testing. And thus it would be expected up front that most of your application, at least the core glue of the application, is parameterized over generics.&lt;/p&gt;

&lt;p&gt;This is not at all how you would write C++ or Java, so it will not be obvious to new Rust application developers.&lt;/p&gt;

&lt;h2 id=&quot;heterogenous-collections&quot;&gt;Heterogenous collections&lt;/h2&gt;

&lt;p&gt;Applications are full of collections of heterogenous objects. We use dependency injection and type erasure to build collections of these objects. For instance, lists of callback/task objects or collections of modules that are written by many teams, and are built on top of your core system but are decoupled from it and which you cannot name. Abstractions are used to provide this decoupling, and in Rust you quickly land on collections of trait objects to make things compile, since &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Box&amp;lt;dyn T&amp;gt;&lt;/code&gt; is a consistent size. This feels familiar and consistent with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector&amp;lt;std::unique_ptr&amp;lt;VirtualClass&amp;gt;&amp;gt;&lt;/code&gt; in C++. Except the latter works well in the language (while causing security bugs, sorry I had to say it) while the former causes a lot of friction for the developer in Rust.&lt;/p&gt;

&lt;p&gt;The “write your code with a generic parameter” solution will not suffice here. As soon as a Rust application developer goes from a single generic object to multiple heterogenous ones, the strategy for abstraction breaks down and creates confusion.&lt;/p&gt;

&lt;p&gt;There are better (I think? I haven’t used these myself) ways to do this heterogeous-type-erasure-with-ownership than trait objects, though they appear to be incredibly complicated to implement. The &lt;a href=&quot;https://bevyengine.org/&quot;&gt;Bevy game engine&lt;/a&gt; does this in order to build an ECS model.&lt;/p&gt;

&lt;p&gt;Here’s some articles about doing type erasure for dependency injection:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://nickbryan.co.uk/software/using-a-type-map-for-dependency-injection-in-rust/&quot;&gt;Dependency Injection in Rust with Type-Maps&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://promethia-27.github.io/dependency_injection_like_bevy_from_scratch/introductions.html&quot;&gt;Dependency Injection like Bevy Engine from Scratch&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In this case, I think there’s an opportunity in both Rust education and in the language to make this less difficult.&lt;/p&gt;</content><author><name>Dana Jansens</name></author><category term="Rust" /><summary type="html">I read yet another reddit post where an OO developer struggles with building abstractions in Rust. These things feel familiar to me, I have had the same struggles moving from being a C++ developer to building a non-trivial Rust application and trying to use abstractions throughout to reduce coupling. And I see colleagues at work ask the same things as they start to pick up Rust.</summary></entry><entry><title type="html">Integer overflow and arithmetic safety in C++</title><link href="http://orodu.net/2023/11/29/overflow.html" rel="alternate" type="text/html" title="Integer overflow and arithmetic safety in C++" /><published>2023-11-29T00:00:00+00:00</published><updated>2023-11-29T00:00:00+00:00</updated><id>http://orodu.net/2023/11/29/overflow</id><content type="html" xml:base="http://orodu.net/2023/11/29/overflow.html">&lt;p&gt;Another &lt;a href=&quot;https://www.forbes.com/sites/daveywinder/2023/11/29/new-critical-google-chrome-security-warning-as-0-day-attacks-confirmed/?sh=78094ae24044&quot;&gt;0-day security bug in Chrome&lt;/a&gt; has been found being used “in the wild”. Once again, it was built on the
bug class of &lt;strong&gt;integer overflow&lt;/strong&gt;.
This is &lt;a href=&quot;https://duckduckgo.com/?q=integer+overflow+chrome+0-day&amp;amp;df=y&amp;amp;ia=web&quot;&gt;not the first integer overflow&lt;/a&gt;
used in an 0-day against Chrome, even within this year.&lt;/p&gt;

&lt;p&gt;When a security bug is used in the wild, it means that it is being used to
attack users of that software &lt;em&gt;and&lt;/em&gt; that the vendor found out about it. These
attacks are going on all the time, with no real way to actually know what bugs
&lt;em&gt;exist&lt;/em&gt; and are being used like that until you find one being used. The ones
that go undetected can remain in active use indefinitely, until the
software vendor makes systemic changes to make those types of bugs go away.&lt;/p&gt;

&lt;p&gt;Software vendors never talk about &lt;em&gt;how&lt;/em&gt; a 0-day security bug is being used,
so news media doesn’t have much to go on and could only really speculate.
However these bugs are worth &lt;a href=&quot;https://techcrunch.com/2023/09/27/russian-zero-day-seller-offers-20m-for-hacking-android-and-iphones/&quot;&gt;millions of dollars&lt;/a&gt;.
They can be used for things that hurt people and society, like
&lt;a href=&quot;https://www.theregister.com/2023/10/25/prorussia_group_exploits_roundcube_zeroday/&quot;&gt;espionage&lt;/a&gt; and
&lt;a href=&quot;https://arstechnica.com/gadgets/2021/07/microsoft-says-hackers-in-china-exploited-critical-solarwinds-0-day/&quot;&gt;intelligence&lt;/a&gt;,
&lt;a href=&quot;https://www.intelligenceonline.com/surveillance--interception/2022/10/27/russian-firm-operation-zero-braves-ukraine-war-to-launch-into-zero-day-market,109839016-eve&quot;&gt;war&lt;/a&gt;,
&lt;a href=&quot;https://www.cbc.ca/documentaries/the-passionate-eye/i-know-why-jamal-was-killed-1.6232638&quot;&gt;assassination&lt;/a&gt;,
&lt;a href=&quot;https://www.theguardian.com/technology/2021/dec/10/software-flaw-most-critical-vulnerability-log-4-shell&quot;&gt;theft and ransomware&lt;/a&gt;,
&lt;a href=&quot;https://www.technologyreview.com/2021/05/06/1024621/china-apple-spy-uyghur-hacker-tianfu/&quot;&gt;oppression&lt;/a&gt;,
&lt;a href=&quot;https://www.cbc.ca/news/science/candiru-sold-tools-hack-windows-microsoft-1.6104434&quot;&gt;surveillance&lt;/a&gt;,
and supporting &lt;a href=&quot;https://thehackernews.com/2021/08/bahraini-activists-targeted-using-new.html&quot;&gt;human rights abuses&lt;/a&gt;.
And when a piece of software becomes ubiquitous, the reach of attacks
in that software grow, as should the responsibility to stop them from
happening.&lt;/p&gt;

&lt;h2 id=&quot;integer-overflow-in-c&quot;&gt;Integer overflow in C++&lt;/h2&gt;

&lt;p&gt;Chrome is written in C and C++ (with a tiny touch of Rust now), and thus is highly vulnerable to the
classes of security bugs that affect these languages. Integers in C and C++ follow some very
problematic rules that cause these types of bugs:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;They will implicitly convert to smaller types, truncating values and changing the valid range.&lt;/li&gt;
  &lt;li&gt;They will implicitly change sign, changing the meaning of their value and their valid range.&lt;/li&gt;
  &lt;li&gt;Unsigned integers will implicitly wrap around on overflow.&lt;/li&gt;
  &lt;li&gt;Signed integers will cause Undefined Behaviour (essentially, mis-compilation) on overflow.&lt;/li&gt;
  &lt;li&gt;Additionally, converting from floating point values to integers can also cause Undefined Behaviour.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Integer overflow is a common 0-day target in C and C++ codebases. Developers don’t write software
expecting integers to overflow. So when they do, the program gets into an
unexpected state, and can become a useful
&lt;a href=&quot;https://en.wikipedia.org/wiki/Weird_machine&quot;&gt;weird machine&lt;/a&gt; for an attacker.&lt;/p&gt;

&lt;p&gt;Every major C or C++ codebase that is ubiquitous across user devices, and interacts with
remote data like images or websites will be similarly valuable to attackers, and vulnerable
to the same classes of bugs. And we see active 0-day bugs being reported in such
software products regularly (for example,
&lt;a href=&quot;https://www.tomsguide.com/news/apple-fixes-two-actively-exploited-zero-day-flaws-update-your-iphone-and-mac-now&quot;&gt;iOS&lt;/a&gt;
and
&lt;a href=&quot;https://www.bleepingcomputer.com/news/security/september-android-updates-fix-zero-day-exploited-in-attacks/&quot;&gt;Android&lt;/a&gt;,
&lt;a href=&quot;https://www.bleepingcomputer.com/news/security/apple-releases-safari-1561-to-fix-zero-day-bug-used-in-attacks/&quot;&gt;Safari&lt;/a&gt; and
&lt;a href=&quot;https://thehackernews.com/2022/03/2-new-mozilla-firefox-0-day-bugs-under.html&quot;&gt;Firefox&lt;/a&gt;
).&lt;/p&gt;

&lt;p&gt;The safety of C++ is a hot topic right now, with &lt;a href=&quot;https://www.cisa.gov/news-events/news/urgent-need-memory-safety-software-products&quot;&gt;CISA calling out languages&lt;/a&gt;
that lack the ability to prevent these types of security bugs, specifically C and C++,
and advocating for the use of memory-safe languages.&lt;/p&gt;

&lt;p&gt;This has led to a bit of a reckoning in the C++ language design community. The CppNow 2023
conference had multiple talks on C++ Successor Languages, which positions moving off C++
as the strategy to stop putting customers and users into harm’s way.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Carbon: &lt;a href=&quot;https://www.youtube.com/watch?v=1ZTJ9omXOQ0&quot;&gt;Carbon Language Successor Strategy: From C++ Interop to Memory Safety - Chandler Carruth&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Swift: &lt;a href=&quot;https://www.youtube.com/watch?v=lgivCGdmFrw&quot;&gt;Introducing a Memory-Safe Successor Language in Large C++ Code Bases - John McCall&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Cpp2: &lt;a href=&quot;https://www.youtube.com/watch?v=fJvPBHErF2U&quot;&gt;The Evolution of C++ - A Typescript for C++ - Herb Sutter&lt;/a&gt;
    &lt;ul&gt;
      &lt;li&gt;This is the least “moving off C++” of the three, but still entails working in a new
syntax/language even if it shares a compiler.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There’s also been many talks on making C++ itself safer at CppNow, CppCon, and elsewhere.
For example:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=Gh79wcGJdTg&quot;&gt;Safety and Security: The Future of C++ - JF Bastien&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=WsswOOTxlIM&quot;&gt;Timur Doumler: C++ and Safety&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=jFi5cILjbA4&quot;&gt;Coding for Safety, Security, and Sustainability in C++ - Panel Discussion&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=MO-qehjc04s&quot;&gt;All the Safeties: Safety in C++ - Sean Parent&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=eo-4ZSLn3jc&amp;amp;t=3050s&quot;&gt;Bjarne Stroustrup :: Approaching C++ Safety&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Most recently, Dr. Stroustrup gave the &lt;a href=&quot;https://www.youtube.com/watch?v=I8UvQKvOSSw&quot;&gt;Delivering Safe C++&lt;/a&gt;
talk at CppCon 2023.&lt;/p&gt;

&lt;p&gt;The first challenge for the C++ language design community has seemed to be the task of
figuring out what “safety” means. The vast majority of software exploits are written against
&lt;em&gt;memory safety&lt;/em&gt; bugs, and CISA has been clear that &lt;em&gt;memory safety&lt;/em&gt; is the issue they are
concerned with. I think the C++ community is slowly coming to &lt;a href=&quot;https://www.youtube.com/watch?v=Gh79wcGJdTg&quot;&gt;focus on this&lt;/a&gt; as well, though its leaders do still want to consider a broader scope of safety
for better or for worse.&lt;/p&gt;

&lt;p&gt;In “Delivering Safe C++”, Dr. Stroustrup talks about profiles to address some forms of safety, and
one of those is &lt;strong&gt;arithmetic&lt;/strong&gt;. This to the class of bugs with integer and floating point types, like
&lt;em&gt;integer overflow&lt;/em&gt;, for which C++ currently lacks the guardrails to protect developers
and their customers alike.&lt;/p&gt;

&lt;h2 id=&quot;integer-overflow-elsewhere&quot;&gt;Integer overflow elsewhere&lt;/h2&gt;

&lt;p&gt;There’s two other important languages that C++ developers should be thinking about for production
code right now: Swift and Rust.&lt;/p&gt;

&lt;p&gt;John McCall talks about Swift’s position on integer overflow in &lt;a href=&quot;https://www.youtube.com/watch?v=lgivCGdmFrw&quot;&gt;this video from CppNow&lt;/a&gt;. The language does not allow integer overflow.
The designers consider an overflowing value to be a bug in the program which leads to
the program misbehaving, and they consider it better to stop the program
than to let it continue running in a state it was not designed for. This is completely in
line with what you’d want to protect your software and your device from being exploited.&lt;/p&gt;

&lt;p&gt;Rust is known for being memory-safe yet only traps on integer overflow in debug builds,
by default. Chrome enables overflow trapping in Rust for release builds too, for now, but
how does Rust get away without catching overflow in release builds by default? I believe it
is primarily because Rust makes pretty much all pointer access go through slices, which
include bounds checks. There are no &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;memcpy&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;memset&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;malloc&lt;/code&gt;, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;alloca&lt;/code&gt; functions that
take a pointer without a bound. You use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; for memory allocation and slices provide
safe APIs for copying contiguous ranges of data around. It is still sketchy to allow overflow
to go unchecked however, and definitely leaves your program in an unexpected state which
has the potential to be abused. However it’s critical to note that Rust &lt;em&gt;defines&lt;/em&gt; the behaviour of
overflow in release builds even if it’s allowed; there is no Undefined Behaviour with signed
integers as we see in C++.&lt;/p&gt;

&lt;p&gt;What about upcoming potential languages?&lt;/p&gt;

&lt;p&gt;So far, &lt;a href=&quot;https://github.com/carbon-language/carbon-lang/blob/0c0998d7cdc348b5c0128e9de36572cce628a696/docs/design/expressions/arithmetic.md?plain=1#L133&quot;&gt;Carbon takes the same position as C++&lt;/a&gt;
for release builds, with Undefined Behaviour on signed overflow, and no trapping on overflow
in general. It’s worth noting the plan includes trapping in debug builds, like with Rust.
Though the high level of interop with C/C++ may leave the language with a similar level of
vulnerability as C++, depending on how code ends up being ported to or written in Carbon.&lt;/p&gt;

&lt;p&gt;Zig makes &lt;a href=&quot;https://github.com/ziglang/zig/issues/46&quot;&gt;overflow into Undefined Behaviour&lt;/a&gt; in release
builds and panic/trap in debug builds. They provide wrapping arithmetic operators to make it
explicit and lightweight, but the risk of Undefined Behaviour in release builds would leave the
language vulnerable to the same class of bugs.&lt;/p&gt;

&lt;p&gt;C has all the same problems as C++ but fewer ways to address them, since it does not
allow creating abstractions with the same power as in C++. C has recently gained
&lt;a href=&quot;https://gustedt.wordpress.com/2022/12/18/checked-integer-arithmetic-in-the-prospect-of-c23/&quot;&gt;library functions for performing checked arithmetic&lt;/a&gt;.
But opting in explicitly to safety means the defaults will still do the dangerous thing
and bugs will persist.&lt;/p&gt;

&lt;h2 id=&quot;solving-integer-overflow-and-the-class-of-arithmetic-bugs-in-c&quot;&gt;Solving integer overflow (and the class of arithmetic bugs) in C++&lt;/h2&gt;

&lt;p&gt;C++ gives us the opportunity to build abstractions that change or redefine behaviour of
the code we write. A great example of such an abstraction in the memory-safety space is
&lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/main/base/memory/raw_ptr.md&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;raw_ptr&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt;, aka
&lt;a href=&quot;https://security.googleblog.com/2022/09/use-after-freedom-miracleptr.html&quot;&gt;MiraclePtr&lt;/a&gt;. This is
an abstraction around a native pointer that works with the allocator to keep the memory from being
reclaimed and reused while there’s a dangling pointer to it.&lt;/p&gt;

&lt;p&gt;Over 2022 and 2023 I have been building an abstraction for numeric types that drop in as a
replacement for primitive types (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;float&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uint32_t&lt;/code&gt;, etc.) and which eliminate
the class of arithmetic unsafety bugs in C++.&lt;/p&gt;

&lt;p&gt;These new types are a drop-in replacement as they implicitly convert to and from primitive integer
types, &lt;em&gt;when there is no truncation or sign change required&lt;/em&gt;. This allows them to be used while
working with APIs built on primitive types. And it allows the migration of APIs to use them
even while callers continue to use primitive types. However, they will catch at
compile time any callers that were previously losing potential data in primitive types.&lt;/p&gt;

&lt;p&gt;These types protect against integer overflow by panicking in the default operations (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;+&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;*&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;%&lt;/code&gt;). But since they are a user-defined type, they can provide a rich API for handling
overflow dynamically, saturating, or wrapping. And they can give you Undefined Behaviour on signed
overflow if you really want it, but you have to ask for it in an explicit way that both you and
your code reviewer can see.&lt;/p&gt;

&lt;p&gt;Read more about the types in the API documentation here:
&lt;a href=&quot;https://suslib.cc/sus-namespace.num.html&quot;&gt;https://suslib.cc/sus-namespace.num.html&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For basic stuff, they work just like primitive types. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i32&lt;/code&gt; takes the place of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int&lt;/code&gt; in
most cases. And they work with streams or &lt;a href=&quot;https://github.com/fmtlib/fmt&quot;&gt;fmtlib&lt;/a&gt; out of
the box.&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;n&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; 
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 

    &lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prev1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prev2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;prev2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;prev1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;prev1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; 
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cerr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot; &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{} &quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fib&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;n&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; 
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; 
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Designing is expensive and risky, and creating new APIs for folks to learn is a problem. We have
enough APIs!
So rather than make something completely new, the numeric APIs are modeled after
&lt;a href=&quot;https://doc.rust-lang.org/stable/std/primitive.i32.html&quot;&gt;Rust’s numeric APIs&lt;/a&gt;.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optional&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;calculate_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usize&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usize&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;usize&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oflo1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;elem_size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;overflowing_mul&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;length&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oflo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acc1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;overflowing_add&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;offset&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;oflo1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;oflo2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;nullopt&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;acc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;If you are familiar with Rust, then you already know the provided APIs, there’s nothing
more to learn. If you’re working in a mixed-language codebase, you can use the same APIs and
expect the same behaviour regardless of where you happen to be writing code that day.&lt;/p&gt;

&lt;h3 id=&quot;making-overflow-checks-optional&quot;&gt;Making overflow checks optional&lt;/h3&gt;

&lt;p&gt;I really want to stop seeing integer overflow bugs in the news, and for me that means catching
overflow as it happens in production, as integers in Swift will do.&lt;/p&gt;

&lt;p&gt;However applying these checks to existing code can be risky:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;There are unknown stability risks. What if there are load-bearing overflows that you were not
aware of? Maybe they are a bug that you would fix, once you know about it, but crashing the
program would be catastrophic. Or maybe you want to preserve the overflows and apply wrapping
behaviour.&lt;/li&gt;
  &lt;li&gt;There are unknown performance risks. Using safe numeric types with overflow checks does imply
&lt;em&gt;some&lt;/em&gt; performance costs, and without the ability to turn them on and off, it’s impossible to
verify what the cost is and do the performance work needed to drive those costs down through
different algorithms (such as iterators instead of indexing) or with explicit unchecked
arithmetic.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With the &lt;a href=&quot;https://github.com/chromium/subspace/pull/410&quot;&gt;Make overflow checks optional at compile time&lt;/a&gt; PR that landed today, the overflow checks in these
types are now able to be turned on and off at compile time. Crucially, like Rust, when disabled
the overflow behaviour of signed integers is still well-defined, the conversions between integers
and floating point types are all well-defined, uninitialized memory is always avoided, 
and invalid type conversions are still prevented at compile time.&lt;/p&gt;

&lt;p&gt;These safe numeric types correspond to the
&lt;a href=&quot;https://www.youtube.com/watch?v=I8UvQKvOSSw#t=1h5m56s&quot;&gt;overflows and unanticipated conversions&lt;/a&gt;
safety profile proposed by Dr. Stroustrup, where different types of codebases can opt into
different behaviour.
While a piece of software that exposes you to attackers on the internet (like a browser,
or a phone OS) should do everything possible to keep you safe, there are classes of software
that do not have to deal with security threats. Yet even there, when debugging, the ability to
turn on runtime checks can save many hours.&lt;/p&gt;

&lt;p&gt;By using a new set of types, instead of a compiler warning, it is possible to apply
safer arithmetic and numeric type safety across an existing codebase in an incremental
manner. By making overflow checks optional, it is possible to look for overflow in
fuzzers and tests and work toward preventing overflow in production in an incremental
manner.&lt;/p&gt;

&lt;p&gt;There’s still &lt;a href=&quot;https://github.com/chromium/subspace/milestone/1&quot;&gt;some work to do&lt;/a&gt;
on these types and their associated types in the library, and
a &lt;a href=&quot;https://github.com/chromium/subspace/issues/394&quot;&gt;sharp edge in the language&lt;/a&gt;
to work through,
but they are already proving
useful in my experience.
The type safety prevents doing the wrong thing by accident, and the overflow checks make
debugging any surprises almost trivial, instead of the gnarly debugging sessions that C++
normally makes me think of.
I am intending to reach a public release of them very soon.&lt;/p&gt;</content><author><name>Dana Jansens</name></author><category term="C++" /><category term="Subspace" /><category term="Undefined Behaviour" /><category term="Memory safety" /><summary type="html">Another 0-day security bug in Chrome has been found being used “in the wild”. Once again, it was built on the bug class of integer overflow. This is not the first integer overflow used in an 0-day against Chrome, even within this year.</summary></entry><entry><title type="html">Complexity of reference containers</title><link href="http://orodu.net/2023/09/24/references-option-copy.html" rel="alternate" type="text/html" title="Complexity of reference containers" /><published>2023-09-24T00:00:00+00:00</published><updated>2023-09-24T00:00:00+00:00</updated><id>http://orodu.net/2023/09/24/references-option%20copy</id><content type="html" xml:base="http://orodu.net/2023/09/24/references-option-copy.html">&lt;p&gt;While we can mourn the &lt;a href=&quot;https://thephd.dev/to-bind-and-loose-a-reference-optional&quot;&gt;lack of holding references in std::optional&lt;/a&gt;, I want yall to know that holding references in a C++ type (or, appearing to anyway because it’s got to be stored as a pointer) is incredibly tricky and subtle.&lt;/p&gt;

&lt;p&gt;Subspace can represent references in &lt;a href=&quot;https://suslib.cc/sus-namespace.option.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&amp;lt;T&amp;amp;&amp;gt;&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://suslib.cc/sus-result-Result.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Result&amp;lt;T&amp;amp;, E&amp;gt;&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://suslib.cc/sus-choice_type-Choice.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Choice&amp;lt;...&amp;gt;&lt;/code&gt;&lt;/a&gt;, and &lt;a href=&quot;https://suslib.cc/sus-tuple_type-Tuple.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Tuple&amp;lt;T&amp;amp;, U&amp;amp;, ...&amp;gt;&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I think this is a &lt;em&gt;very&lt;/em&gt; powerful and useful thing, especially as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&amp;lt;T&amp;amp;&amp;gt;&lt;/code&gt; is represented internally as &lt;em&gt;just a pointer&lt;/em&gt;, no extra bool. This makes programming mistakes into very clear and actionable errors instead of UB and failures that require debugging and backtracking to even know what state was wrong.&lt;/p&gt;

&lt;p&gt;But I think this set of vocab types is it, the value/cost drops off quickly, so I don’t think I will want to support references in any other types.&lt;/p&gt;

&lt;p&gt;What cost? Implementing support in a template for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; being a value or a reference adds a lot of complexity to the implementation - which to be fair would be intractable before C++20 in my opinion. It adds a ton of testing complexity to make sure you’re actually handling the references correctly and that you’re never testing a concept against a reference which then answers for the pointee type, or acting on a reference in a way that reaches through to the pointee.&lt;/p&gt;

&lt;p&gt;But also, receiving and storing a reference can lead to implicit and completely hidden memory safety bugs.&lt;/p&gt;

&lt;h2 id=&quot;memory-safety-bugs&quot;&gt;Memory safety bugs&lt;/h2&gt;

&lt;p&gt;Let’s take for example an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&amp;lt;const int64_t&amp;amp;&amp;gt;&lt;/code&gt;. You can construct this &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt; by giving it a reference to an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int64_t&lt;/code&gt; lvalue, or to an rvalue.&lt;/p&gt;

&lt;p&gt;This is already a problem but one that is visible in the code at least. If you pass an rvalue to something that captures it as a reference, it will dangle. With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[[clang::lifetimebound]]&lt;/code&gt; annotations, you get a warning/error too. Which I put every effort into making exhaustive, though it’s a challenge since it’s not possible to test for errors like these inside the language.&lt;/p&gt;

&lt;p&gt;There’s a worse situation though. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const T&amp;amp;&lt;/code&gt; will also apply implicit conversions!&lt;/p&gt;

&lt;p&gt;So if you construct an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&amp;lt;int64_t&amp;amp;&amp;gt;&lt;/code&gt; but pass it an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int32_t&lt;/code&gt;, C++ will helpfully construct a temporary &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int64_t&lt;/code&gt; and use the reference to that.&lt;/p&gt;

&lt;p&gt;Now you have Undefined Behaviour from a dangling reference, but one you didn’t even write!&lt;/p&gt;

&lt;p&gt;I wrote a concept that catches this scenario so that any methods that receive and hold a reference can reject this in a clear way. And it can be rejected in every compiler, not just through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[[clang::lifetimebound]]&lt;/code&gt;. But this is yet another thing to test for and think about while building the APIs.&lt;/p&gt;

&lt;p&gt;The concept is &lt;a href=&quot;https://suslib.cc/sus-construct-SafelyConstructibleFromReference.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SafelyConstructibleFromReference&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I wrote down all the rules I had in my head today about building reference types in a markdown doc so that I can come back to them and can use them in code reviews in the future.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/chromium/subspace/blob/main/STYLE.md#containers-that-hold-references&quot;&gt;https://github.com/chromium/subspace/blob/main/STYLE.md#containers-that-hold-references&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;seeing-it-in-compiler-explorer&quot;&gt;Seeing it in Compiler Explorer&lt;/h2&gt;

&lt;p&gt;Here’s a little example of these cases: &lt;a href=&quot;https://godbolt.org/z/q6q7GWaEY&quot;&gt;https://godbolt.org/z/q6q7GWaEY&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see Clang can make a warning for each of the UB cases, but the other two compilers don’t at all.&lt;/p&gt;

&lt;p&gt;With the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SafelyConstructibleFromReference&lt;/code&gt; concept, the last case which is very sneaky UB will be rejected on all compilers, though not the others.&lt;/p&gt;

&lt;p&gt;If you remove some of the abstractions inside the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option&lt;/code&gt; there, GCC ends up seeing one of the three UB cases, but as we can see it’s easy for it to lose that.&lt;/p&gt;</content><author><name>Dana Jansens</name></author><category term="C++" /><category term="Subspace" /><category term="Undefined Behaviour" /><summary type="html">While we can mourn the lack of holding references in std::optional, I want yall to know that holding references in a C++ type (or, appearing to anyway because it’s got to be stored as a pointer) is incredibly tricky and subtle.</summary></entry><entry><title type="html">On push_back_unchecked: Performance with FromIterator and Collect</title><link href="http://orodu.net/2023/08/29/push-back-unchecked.html" rel="alternate" type="text/html" title="On push_back_unchecked: Performance with FromIterator and Collect" /><published>2023-08-29T00:00:00+00:00</published><updated>2023-08-29T00:00:00+00:00</updated><id>http://orodu.net/2023/08/29/push-back-unchecked</id><content type="html" xml:base="http://orodu.net/2023/08/29/push-back-unchecked.html">&lt;p&gt;A very nice blog post went by yesterday titled
“&lt;a href=&quot;https://codingnest.com/the-little-things-the-missing-performance-in-std-vector/&quot;&gt;The Little Things: The Missing Performance in std::vector&lt;/a&gt;”.&lt;/p&gt;

&lt;p&gt;You should read it, but in case you don’t, the premise is that &lt;a href=&quot;https://en.cppreference.com/w/cpp/container/vector/push_back&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector::push_back&lt;/code&gt;&lt;/a&gt; is wasteful when you’re appending
a bunch of things to a vector, since you can reserve space for them all but it keeps checking if it
needs to allocate more space on every loop iteration. The author calls for a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector::push_back_unchecked&lt;/code&gt; method that appends under the assumption that space is already
allocated. This exposes Undefined Behaviour if you do it wrong, of course, and the author argues
that this is both necessary and good. And I agree.&lt;/p&gt;

&lt;p&gt;This blog, and my efforts in the C++ language space, are all around reducing the negative impact of
Undefined Behaviour and eliminating memory-safety bugs. So the above may seem counterintuitive at
first glance.&lt;/p&gt;

&lt;p&gt;I recently argued that the nature of the C++ standard library is basically &lt;a href=&quot;https://sunny.garden/@blinkygal/110871585437820584&quot;&gt;to expose Undefined
Behaviour&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;This is the nature of bare C++; it is as close to the hardware as possible for historical and
good reasons. The C++ standard intentionally leaves no room for a lower level language (except
hardware assembly), and it exposes all the complexity of the compiler through the std lib
(e.g. type_traits). This is an “assembly language for systems programming”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And in that same vein, adding &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector::push_back_unchecked&lt;/code&gt; would expose the nature of the
conceptual C++ machine, and allow authors to control that machine more effectively.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Undefined Behaviour is Good.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Chandler Carruth argues for Undefined Behaviour from a slightly different perspective in his CppCon
2016 talk, &lt;a href=&quot;https://www.youtube.com/watch?v=yG1OZ69H_-o&quot;&gt;Garbage In, Garbage Out: Arguing about Undefined Behavior With Nasal Demons&lt;/a&gt;. Undefined Behaviour gives us performance, as we can
see clearly from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push_back_unchecked&lt;/code&gt; proposal.&lt;/p&gt;

&lt;p&gt;But that sentence above is not complete without a qualification which usually gets missed in the
C++ world. Completed, it reads:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Undefined Behaviour is Good when it is Encapsulated.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I believe &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector&lt;/code&gt; should expose that Undefined Behaviour along with, and because it exposes,
many
other Undefined Behaviours. &lt;strong&gt;However that also implies that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector&lt;/code&gt; is not suitable for use in
most application code&lt;/strong&gt;. It is a building block on which performant APIs can be built which
encapsulate the inherent unsafety of its own API that leaks Undefined Behaviour all throughout.&lt;/p&gt;

&lt;h2 id=&quot;fromiterator-and-collect&quot;&gt;FromIterator and Collect&lt;/h2&gt;

&lt;p&gt;Subspace provides the
&lt;a href=&quot;https://github.com/chromium/subspace/blob/36d965c504a570731a0e6f377fc994a0ad1daf05/sus/iter/from_iterator.h#L28-L43&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromIterator&lt;/code&gt;&lt;/a&gt; concept
and then implements this concept for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Vec&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector&lt;/code&gt; as well as all the types in the
&lt;a href=&quot;https://en.cppreference.com/w/cpp/container&quot;&gt;standard containers library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Like most of the Subspace library, this is a reimplementation of the Rust &lt;a href=&quot;https://doc.rust-lang.org/std/iter/trait.FromIterator.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromIterator&lt;/code&gt;&lt;/a&gt; trait. If not already familiar with the
trait and its many uses, the key points for us here are:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;You can construct a container from another container, or an iterator.&lt;/li&gt;
  &lt;li&gt;The construction happens in a single atomic step, from the perspective of application logic,
  which is typically the iterator &lt;a href=&quot;https://github.com/chromium/subspace/blob/36d965c504a570731a0e6f377fc994a0ad1daf05/sus/iter/iterator_defn.h#L1097-L1120&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collect&lt;/code&gt;&lt;/a&gt;
  method.
    &lt;ul&gt;
      &lt;li&gt;See also the Rust
   &lt;a href=&quot;https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collect&lt;/code&gt;&lt;/a&gt;
   for more code examples that will look similar in C++ but aren’t in the Subspace docs (yet).&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By providing the ability to construct a container like a vector in an atomic operation from an
arbitrary set of inputs, we provide the ability for the library to give a safe and simple API
abstraction around a powerful operation which can be implemented entirely on top of APIs that
expose Undefined Behaviour. Because of the atomic nature, the use of those unsafe APIs is fully
encapsulated from the application layer, allowing the use of them in a controlled manner. Much
like in how “safe Rust” applications are built on top of a stdlib full of unsafe Rust,
yet retain memory safety for themselves,
this creates the opportunity to build a safer security-critical C++ application without sacrificing
performance. In fact, &lt;strong&gt;as we’ll see, we can gain performance&lt;/strong&gt;.&lt;/p&gt;

&lt;h2 id=&quot;performance-optimizing-fromiterator-and-collect&quot;&gt;Performance Optimizing FromIterator and Collect&lt;/h2&gt;

&lt;p&gt;The benchmarks provided in the aforementioned “Missing Performance in std::vector” article showed
on Clang 15:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A 5x improvement when building a small to medium vector from a simple integer mapping.&lt;/li&gt;
  &lt;li&gt;A 1.3x improvement when building a large vector of the same.&lt;/li&gt;
  &lt;li&gt;A nearly 3x improvement when building a small to medium vector from a simple mapping function
that unwraps a struct.&lt;/li&gt;
  &lt;li&gt;A 1.1x improvement when building a large vector of the same.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was curious how &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromIterator&lt;/code&gt; would compare with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Vec&lt;/code&gt;. 
If &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push_back_unchecked&lt;/code&gt; existed, the implementation of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromIterator&lt;/code&gt; for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector&lt;/code&gt;
could absolutely make use of
the method, with no risk of introducing Undefined Behaviour and memory safety bugs into the
application as a result. For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Vec&lt;/code&gt; we have full access to the underlying data structure so we
can do all the unsafe things internally that we want, and we should be able to see the same impact
as with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push_back_unchecked&lt;/code&gt;. Given that, how does &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromIterator&lt;/code&gt; compare to
the naive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;v.reserve(..); for (..) { v.push_back(..) }&lt;/code&gt; approach?&lt;/p&gt;

&lt;p&gt;I copied the benchmarks from the above article and added them to the Subspace
&lt;a href=&quot;https://github.com/martinus/nanobench&quot;&gt;nanobench&lt;/a&gt; test suite.&lt;/p&gt;

&lt;p&gt;At first, it was slow. Really slow. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; does not define that it is empty after a move, instead
the moved-from Vec is left invalid and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; checks for use-after-move. Additionally, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; tracks
outstanding iterators and terminates if mutated instead of invalidating the iterator and proceeding
with Undefined Behaviour. But the inner loop of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromIterator&lt;/code&gt; was checking these things over and
over again. So I added some internal methods to break apart checks and implementation, and to split
up methods for better inlining. Then I used local variables to avoid indirections through
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;this&lt;/code&gt;-pointers.&lt;/p&gt;

&lt;p&gt;The PR containing the changes and the benchmarks is &lt;a href=&quot;https://github.com/chromium/subspace/pull/337&quot;&gt;https://github.com/chromium/subspace/pull/337&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s &lt;a href=&quot;https://github.com/chromium/subspace/pull/337#issuecomment-1696793264&quot;&gt;the results&lt;/a&gt; of
building &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Vec&lt;/code&gt; via &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromIterator&lt;/code&gt; compared to building a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector&lt;/code&gt;,
on my M1 Mac with Clang 18:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;A nearly 4.5x improvement when building a small to medium vector from a simple integer mapping.&lt;/li&gt;
  &lt;li&gt;A 1.7x improvement when building a large vector of the same.&lt;/li&gt;
  &lt;li&gt;A 1.5-2x improvement when building a small to medium vector from a simple mapping function
that unwraps a struct.&lt;/li&gt;
  &lt;li&gt;A 1.03x improvement when building a large vector of the same.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Comparing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromIterator&lt;/code&gt; on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector&lt;/code&gt; is done to verify the iterator approach isn’t changing the
nature of the operation, and indeed we see that putting the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push_back&lt;/code&gt; loop into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;collect()&lt;/code&gt;
operation has no visible impact. But the structure enables something more.&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;benchmark&lt;/th&gt;
      &lt;th&gt;n&lt;/th&gt;
      &lt;th&gt;std::vector&lt;/th&gt;
      &lt;th&gt;std::vector + FromIterator&lt;/th&gt;
      &lt;th&gt;sus::Vec + FromIterator&lt;/th&gt;
      &lt;th&gt;std::vector / sus::Vec&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;copy + mult&lt;/td&gt;
      &lt;td&gt;1,000&lt;/td&gt;
      &lt;td&gt;426.00 +- 0.85 ns&lt;/td&gt;
      &lt;td&gt;452.67 +- 2.7 ns&lt;/td&gt;
      &lt;td&gt;98.45 +- 0.1 ns&lt;/td&gt;
      &lt;td&gt;432.7%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;copy + mult&lt;/td&gt;
      &lt;td&gt;100,000&lt;/td&gt;
      &lt;td&gt;39.144 +- 0.04 us&lt;/td&gt;
      &lt;td&gt;37.95 +- 0.04 us&lt;/td&gt;
      &lt;td&gt;8.75 +- 0.41 us&lt;/td&gt;
      &lt;td&gt;447.4%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;copy + mult&lt;/td&gt;
      &lt;td&gt;10,000,000&lt;/td&gt;
      &lt;td&gt;6.14 ms +- 0.04 ms&lt;/td&gt;
      &lt;td&gt;6.33 +- 0.03 ms&lt;/td&gt;
      &lt;td&gt;3.67 +- 0.03 ms&lt;/td&gt;
      &lt;td&gt;167.3%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;to_index&lt;/td&gt;
      &lt;td&gt;1,000&lt;/td&gt;
      &lt;td&gt;351.07 +- 0.35 ns&lt;/td&gt;
      &lt;td&gt;350.30 +- 0.35 ns&lt;/td&gt;
      &lt;td&gt;176.97 +- 1.24 ns&lt;/td&gt;
      &lt;td&gt;198.4%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;to_index&lt;/td&gt;
      &lt;td&gt;100,000&lt;/td&gt;
      &lt;td&gt;31.20 +- 0.031 us&lt;/td&gt;
      &lt;td&gt;31.20 +- 0.031 us&lt;/td&gt;
      &lt;td&gt;20.56 +- 0.88 us&lt;/td&gt;
      &lt;td&gt;151.8%&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;to_index&lt;/td&gt;
      &lt;td&gt;10,000,000&lt;/td&gt;
      &lt;td&gt;7.83 +- 0.031 ms&lt;/td&gt;
      &lt;td&gt;8.19 +- 0.057 ms&lt;/td&gt;
      &lt;td&gt;7.65 +- 0.038 ms&lt;/td&gt;
      &lt;td&gt;102.4%&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;The last column shows the relative speedup of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Vec&lt;/code&gt; + &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FromIterator&lt;/code&gt; approach is compared
to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We have a similar speed improvement to what we saw by using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;push_back_unchecked&lt;/code&gt;, and without any
Undefined Behaviour exposed to application developers&lt;/strong&gt;.&lt;/p&gt;</content><author><name>Dana Jansens</name></author><category term="C++" /><category term="Subspace" /><category term="Undefined Behaviour" /><summary type="html">A very nice blog post went by yesterday titled “The Little Things: The Missing Performance in std::vector”. You should read it, but in case you don’t, the premise is that std::vector::push_back is wasteful when you’re appending a bunch of things to a vector, since you can reserve space for them all but it keeps checking if it needs to allocate more space on every loop iteration. The author calls for a std::vector::push_back_unchecked method that appends under the assumption that space is already allocated. This exposes Undefined Behaviour if you do it wrong, of course, and the author argues that this is both necessary and good. And I agree. This blog, and my efforts in the C++ language space, are all around reducing the negative impact of Undefined Behaviour and eliminating memory-safety bugs. So the above may seem counterintuitive at first glance. I recently argued that the nature of the C++ standard library is basically to expose Undefined Behaviour: This is the nature of bare C++; it is as close to the hardware as possible for historical and good reasons. The C++ standard intentionally leaves no room for a lower level language (except hardware assembly), and it exposes all the complexity of the compiler through the std lib (e.g. type_traits). This is an “assembly language for systems programming”. And in that same vein, adding std::vector::push_back_unchecked would expose the nature of the conceptual C++ machine, and allow authors to control that machine more effectively. Undefined Behaviour is Good. Chandler Carruth argues for Undefined Behaviour from a slightly different perspective in his CppCon 2016 talk, Garbage In, Garbage Out: Arguing about Undefined Behavior With Nasal Demons. Undefined Behaviour gives us performance, as we can see clearly from the push_back_unchecked proposal. But that sentence above is not complete without a qualification which usually gets missed in the C++ world. Completed, it reads: Undefined Behaviour is Good when it is Encapsulated. I believe std::vector should expose that Undefined Behaviour along with, and because it exposes, many other Undefined Behaviours. However that also implies that std::vector is not suitable for use in most application code. It is a building block on which performant APIs can be built which encapsulate the inherent unsafety of its own API that leaks Undefined Behaviour all throughout. FromIterator and Collect Subspace provides the FromIterator concept and then implements this concept for sus::Vec and std::vector as well as all the types in the standard containers library. Like most of the Subspace library, this is a reimplementation of the Rust FromIterator trait. If not already familiar with the trait and its many uses, the key points for us here are: You can construct a container from another container, or an iterator. The construction happens in a single atomic step, from the perspective of application logic, which is typically the iterator collect method. See also the Rust collect for more code examples that will look similar in C++ but aren’t in the Subspace docs (yet). By providing the ability to construct a container like a vector in an atomic operation from an arbitrary set of inputs, we provide the ability for the library to give a safe and simple API abstraction around a powerful operation which can be implemented entirely on top of APIs that expose Undefined Behaviour. Because of the atomic nature, the use of those unsafe APIs is fully encapsulated from the application layer, allowing the use of them in a controlled manner. Much like in how “safe Rust” applications are built on top of a stdlib full of unsafe Rust, yet retain memory safety for themselves, this creates the opportunity to build a safer security-critical C++ application without sacrificing performance. In fact, as we’ll see, we can gain performance. Performance Optimizing FromIterator and Collect The benchmarks provided in the aforementioned “Missing Performance in std::vector” article showed on Clang 15: A 5x improvement when building a small to medium vector from a simple integer mapping. A 1.3x improvement when building a large vector of the same. A nearly 3x improvement when building a small to medium vector from a simple mapping function that unwraps a struct. A 1.1x improvement when building a large vector of the same. I was curious how FromIterator would compare with sus::Vec. If push_back_unchecked existed, the implementation of FromIterator for std::vector could absolutely make use of the method, with no risk of introducing Undefined Behaviour and memory safety bugs into the application as a result. For sus::Vec we have full access to the underlying data structure so we can do all the unsafe things internally that we want, and we should be able to see the same impact as with push_back_unchecked. Given that, how does FromIterator compare to the naive v.reserve(..); for (..) { v.push_back(..) } approach? I copied the benchmarks from the above article and added them to the Subspace nanobench test suite. At first, it was slow. Really slow. Vec does not define that it is empty after a move, instead the moved-from Vec is left invalid and Vec checks for use-after-move. Additionally, Vec tracks outstanding iterators and terminates if mutated instead of invalidating the iterator and proceeding with Undefined Behaviour. But the inner loop of FromIterator was checking these things over and over again. So I added some internal methods to break apart checks and implementation, and to split up methods for better inlining. Then I used local variables to avoid indirections through this-pointers. The PR containing the changes and the benchmarks is https://github.com/chromium/subspace/pull/337. Here’s the results of building sus::Vec via FromIterator compared to building a std::vector, on my M1 Mac with Clang 18: A nearly 4.5x improvement when building a small to medium vector from a simple integer mapping. A 1.7x improvement when building a large vector of the same. A 1.5-2x improvement when building a small to medium vector from a simple mapping function that unwraps a struct. A 1.03x improvement when building a large vector of the same. Comparing FromIterator on std::vector is done to verify the iterator approach isn’t changing the nature of the operation, and indeed we see that putting the push_back loop into the collect() operation has no visible impact. But the structure enables something more. benchmark n std::vector std::vector + FromIterator sus::Vec + FromIterator std::vector / sus::Vec copy + mult 1,000 426.00 +- 0.85 ns 452.67 +- 2.7 ns 98.45 +- 0.1 ns 432.7% copy + mult 100,000 39.144 +- 0.04 us 37.95 +- 0.04 us 8.75 +- 0.41 us 447.4% copy + mult 10,000,000 6.14 ms +- 0.04 ms 6.33 +- 0.03 ms 3.67 +- 0.03 ms 167.3% to_index 1,000 351.07 +- 0.35 ns 350.30 +- 0.35 ns 176.97 +- 1.24 ns 198.4% to_index 100,000 31.20 +- 0.031 us 31.20 +- 0.031 us 20.56 +- 0.88 us 151.8% to_index 10,000,000 7.83 +- 0.031 ms 8.19 +- 0.057 ms 7.65 +- 0.038 ms 102.4% The last column shows the relative speedup of the sus::Vec + FromIterator approach is compared to std::vector. We have a similar speed improvement to what we saw by using push_back_unchecked, and without any Undefined Behaviour exposed to application developers.</summary></entry><entry><title type="html">A tale of C/C++ development in three parts</title><link href="http://orodu.net/2023/08/20/cpp-in-three-parts.html" rel="alternate" type="text/html" title="A tale of C/C++ development in three parts" /><published>2023-08-20T00:00:00+00:00</published><updated>2023-08-20T00:00:00+00:00</updated><id>http://orodu.net/2023/08/20/cpp-in-three-parts</id><content type="html" xml:base="http://orodu.net/2023/08/20/cpp-in-three-parts.html">&lt;p&gt;Here’s what it’s like doing C++ development. I am writing a C++ application called
&lt;a href=&quot;https://github.com/chromium/subspace/#subdoc&quot;&gt;Subdoc&lt;/a&gt;. It has
to be in C++ because it has an important C++ dependency: &lt;a href=&quot;https://clang.llvm.org/&quot;&gt;Clang&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I would write it in Rust if it were easy to use Clang from Rust, but alas interop is not at that
point.&lt;/p&gt;

&lt;h1 id=&quot;part-one-finding-greatness&quot;&gt;Part One: Finding Greatness&lt;/h1&gt;

&lt;p&gt;To generate html comments from markdown needs a markdown parser. So I hunted the internets and found
&lt;a href=&quot;https://github.com/mity/md4c/&quot;&gt;md4c&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;What does the README have to say about it:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;It is very fast.&lt;/li&gt;
  &lt;li&gt;It is easy to integrate into your application.&lt;/li&gt;
  &lt;li&gt;It is used by large projects who you’d expect to vet their dependencies, like &lt;a href=&quot;https://www.qt.io/&quot;&gt;Qt&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I integrate md4c into Subdoc, and yes it’s quite easy to do so and it works great!&lt;/p&gt;

&lt;h1 id=&quot;part-two-trouble&quot;&gt;Part Two: Trouble&lt;/h1&gt;

&lt;p&gt;I want to add some extensions to md4c for Subdoc, so that I can generate headers as links to
themselves, like you’d see on GitHub. To do that, md4c needs to provide additional signal to
md4c-html, and md4c-html needs some additional callbacks.&lt;/p&gt;

&lt;p&gt;So I go looking through the &lt;a href=&quot;https://github.com/mity/md4c/issues&quot;&gt;issue tracker&lt;/a&gt; to see what others
are doing.&lt;/p&gt;

&lt;p&gt;And I find an issue near the top: &lt;a href=&quot;https://github.com/mity/md4c/issues/192&quot;&gt;“Project Dead?”&lt;/a&gt;. Folks
talk about trying to reach the developer on Twitter and that he has disappeared from development.&lt;/p&gt;

&lt;p&gt;There are many forks of the project but none appear to be active. So the codebase is essentially
dead in the water, abandonware. You get what you get.&lt;/p&gt;

&lt;p&gt;So I fork and add some extensions to the project for my needs, and it’s working great.&lt;/p&gt;

&lt;h1 id=&quot;part-three-the-cc-security-wow-factor&quot;&gt;Part Three: The C/C++ Security Wow Factor&lt;/h1&gt;

&lt;p&gt;I look a little more in the issue tracker, and I find:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mity/md4c/issues/196&quot;&gt;Array out-of-bounds access leads to segmentation fault in md_build_attribute function&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mity/md4c/issues/195&quot;&gt;Invalid size passed to memcpy in md_process_inlines&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://github.com/mity/md4c/issues/176&quot;&gt;Valgrind error: “Conditional jump or move depends on uninitialised value(s)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And a pull request titled &lt;a href=&quot;https://github.com/mity/md4c/pull/185&quot;&gt;“Update md4c.c”&lt;/a&gt; which is fixing
at least three more memory safety bugs.&lt;/p&gt;

&lt;p&gt;So there’s at least 4 memory safety bugs in this piece of software. It’s kinda widely used. And
now malicious markdown could compromise machines through Subdoc or other consumers of this library,
if those bugs can be triggered with the flags they use. I was able to reproduce a timeout but not
the OOB bugs so far.&lt;/p&gt;

&lt;p&gt;md4c is written in C not in C++. But the reason I am using it is because
&lt;em&gt;I am working in C++&lt;/em&gt;. Otherwise I would be looking at the
&lt;a href=&quot;https://docs.rs/markdown/1.0.0-alpha.12/markdown/index.html&quot;&gt;markdown&lt;/a&gt; crate library. Even though
it is in an alpha state, it would still be more trustworthy for security than this well-used C
library.&lt;/p&gt;

&lt;h1 id=&quot;the-bugs&quot;&gt;The bugs&lt;/h1&gt;

&lt;p&gt;What kinds of bugs are they?&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;One is using an error code as an index into an array. Oops.&lt;/li&gt;
  &lt;li&gt;The rest appear to be many occurances of integer overflow when subtracting two signed values and
then using them as an unsigned value. Classic stuff.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I have added a whole lot of asserts into the library to try prevent this. To actually feel safe
I would need to turn it into C++ and apply
&lt;a href=&quot;https://danakj.github.io/subspace-docs/sus-num.html&quot;&gt;Subspace numerics&lt;/a&gt; which would properly catch
bad values at runtime from malicious inputs.&lt;/p&gt;

&lt;p&gt;But no other users of md4c will get these asserts so… This is how C++ is going right now.&lt;/p&gt;</content><author><name>Dana Jansens</name></author><category term="C++" /><summary type="html">Here’s what it’s like doing C++ development. I am writing a C++ application called Subdoc. It has to be in C++ because it has an important C++ dependency: Clang. I would write it in Rust if it were easy to use Clang from Rust, but alas interop is not at that point. Part One: Finding Greatness To generate html comments from markdown needs a markdown parser. So I hunted the internets and found md4c. What does the README have to say about it: It is very fast. It is easy to integrate into your application. It is used by large projects who you’d expect to vet their dependencies, like Qt. So I integrate md4c into Subdoc, and yes it’s quite easy to do so and it works great! Part Two: Trouble I want to add some extensions to md4c for Subdoc, so that I can generate headers as links to themselves, like you’d see on GitHub. To do that, md4c needs to provide additional signal to md4c-html, and md4c-html needs some additional callbacks. So I go looking through the issue tracker to see what others are doing. And I find an issue near the top: “Project Dead?”. Folks talk about trying to reach the developer on Twitter and that he has disappeared from development. There are many forks of the project but none appear to be active. So the codebase is essentially dead in the water, abandonware. You get what you get. So I fork and add some extensions to the project for my needs, and it’s working great. Part Three: The C/C++ Security Wow Factor I look a little more in the issue tracker, and I find: Array out-of-bounds access leads to segmentation fault in md_build_attribute function Invalid size passed to memcpy in md_process_inlines Valgrind error: “Conditional jump or move depends on uninitialised value(s) And a pull request titled “Update md4c.c” which is fixing at least three more memory safety bugs. So there’s at least 4 memory safety bugs in this piece of software. It’s kinda widely used. And now malicious markdown could compromise machines through Subdoc or other consumers of this library, if those bugs can be triggered with the flags they use. I was able to reproduce a timeout but not the OOB bugs so far. md4c is written in C not in C++. But the reason I am using it is because I am working in C++. Otherwise I would be looking at the markdown crate library. Even though it is in an alpha state, it would still be more trustworthy for security than this well-used C library. The bugs What kinds of bugs are they? One is using an error code as an index into an array. Oops. The rest appear to be many occurances of integer overflow when subtracting two signed values and then using them as an unsigned value. Classic stuff. So I have added a whole lot of asserts into the library to try prevent this. To actually feel safe I would need to turn it into C++ and apply Subspace numerics which would properly catch bad values at runtime from malicious inputs. But no other users of md4c will get these asserts so… This is how C++ is going right now.</summary></entry><entry><title type="html">Comparing floats in Subspace and stdlib</title><link href="http://orodu.net/2023/08/06/float-max.html" rel="alternate" type="text/html" title="Comparing floats in Subspace and stdlib" /><published>2023-08-06T00:00:00+00:00</published><updated>2023-08-06T00:00:00+00:00</updated><id>http://orodu.net/2023/08/06/float-max</id><content type="html" xml:base="http://orodu.net/2023/08/06/float-max.html">&lt;p&gt;I am continuing to go through the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::iter::Iterator&lt;/code&gt; methods and turn them all into &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;constexpr&lt;/code&gt; based on
the performance results from doing so. With &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;constexpr&lt;/code&gt; an iterator of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;chunks_exact()&lt;/code&gt;, and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_while()&lt;/code&gt; is able to outperform a “bare-metal” loop over pointers. More on
&lt;a href=&quot;https://sunny.garden/@blinkygal/110826776270649927&quot;&gt;that over at Mastodon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;As I am doing so, I am writing tests, and this test felt really good to write!&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;static_assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_cmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So I interrogated myself why it did. First off, I was happy that you can’t write this wrong. You can’t call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max()&lt;/code&gt; on floats because they are not strongly ordered. In other words, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;operator&amp;lt;=&amp;gt;()&lt;/code&gt; on floats returns &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::partial_ordering&lt;/code&gt;, not &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::strong_ordering&lt;/code&gt;. That means that floats satisfy the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PartialOrd&lt;/code&gt; concept but not the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ord&lt;/code&gt; concept. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max()&lt;/code&gt; requires &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Ord&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is true for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;float&lt;/code&gt; as well, but &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f32&lt;/code&gt; is nicer to work with for reasons like the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NAN&lt;/code&gt; constant and that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;is_nan()&lt;/code&gt; is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;constexpr&lt;/code&gt; unlike &lt;a href=&quot;https://en.cppreference.com/w/cpp/numeric/math/isnan&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::isnan()&lt;/code&gt;&lt;/a&gt; until C++23.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Doesn't compile, as floats are not strongly ordered. Constraint does not match.&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// error C7500: 'max': no function satisfied its constraints&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f32::total_cmp()&lt;/code&gt; method provides a total ordering over floats and does return &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::strong_ordering&lt;/code&gt;. It does the same thing as &lt;a href=&quot;https://doc.rust-lang.org/std/primitive.f32.html#method.total_cmp&quot;&gt;f32::total_cmp in Rust std&lt;/a&gt;. So we can use it as a callback in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;max_by()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;What does the standard ranges library do when you try to max floats, I wondered. At least on MSVC this is what I got.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Ok sure, looks good.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static_assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// At compile time, NAN is largest.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static_assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NAN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_nan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Unless 3 comes after.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static_assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NAN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}))&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// At run time, different answers. 3 is largest if it comes first.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NAN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// At run time, NAN is larger if it comes first.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NAN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;})).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_nan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Undefined behaviour!!!&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()),&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;0.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Those results are really not okay! They are the kind of thing that doesn’t show up in tests and then completely takes down production. Maybe introduces a security vuln.&lt;/p&gt;

&lt;p&gt;Here’s what I got with Subspace iterators.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c1&quot;&gt;// Reproducible ordering.&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static_assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_cmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;some&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;));&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static_assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NAN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_cmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_nan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static_assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NAN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_cmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_nan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Same thing at runtime as at compile time.&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NAN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_cmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_nan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NAN&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;3.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;2.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_cmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;is_nan&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// Defined behaviour. ^_^b&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static_assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;max_by&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;f32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;total_cmp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;none&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Sure you can just write a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;total_cmp()&lt;/code&gt; method yourself and use that as the comparator with the standard ranges
library. But you don’t have to, it compiles anyway so there’s nothing suggesting something is wrong. And then it
returns garbage or corrupts your compilation with Undefined Behaviour.&lt;/p&gt;

&lt;p&gt;With Subspace &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;total_cmp()&lt;/code&gt; is already there for you, and non-sense inputs don’t compile.&lt;/p&gt;

&lt;p&gt;This stuff is why Rust is pleasant to work in. C++ can be too.&lt;/p&gt;</content><author><name>Dana Jansens</name></author><category term="Subspace" /><category term="C++" /><category term="Rust" /><summary type="html">I am continuing to go through the sus::iter::Iterator methods and turn them all into constexpr based on the performance results from doing so. With constexpr an iterator of chunks_exact(), and take_while() is able to outperform a “bare-metal” loop over pointers. More on that over at Mastodon. As I am doing so, I am writing tests, and this test felt really good to write! static_assert(sus::Vec&amp;lt;f32&amp;gt;::with(2.f, 3.f, 2.f) .into_iter() .max_by(&amp;amp;f32::total_cmp) == sus::some(3.f)); So I interrogated myself why it did. First off, I was happy that you can’t write this wrong. You can’t call max() on floats because they are not strongly ordered. In other words, operator&amp;lt;=&amp;gt;() on floats returns std::partial_ordering, not std::strong_ordering. That means that floats satisfy the PartialOrd concept but not the Ord concept. And max() requires Ord. This is true for float as well, but f32 is nicer to work with for reasons like the NAN constant and that is_nan() is constexpr unlike std::isnan() until C++23. // Doesn't compile, as floats are not strongly ordered. Constraint does not match. // error C7500: 'max': no function satisfied its constraints sus::Vec&amp;lt;f32&amp;gt;::with(2.f, 3.f, 2.f).into_iter().max(); The f32::total_cmp() method provides a total ordering over floats and does return std::strong_ordering. It does the same thing as f32::total_cmp in Rust std. So we can use it as a callback in max_by(). What does the standard ranges library do when you try to max floats, I wondered. At least on MSVC this is what I got. // Ok sure, looks good. static_assert(std::ranges::max( std::vector&amp;lt;f32&amp;gt;({2.f, 3.f, 2.f})) == 3.f); // At compile time, NAN is largest. static_assert(std::ranges::max( std::vector&amp;lt;f32&amp;gt;({2.f, 3.f, f32::NAN})).is_nan()); // Unless 3 comes after. static_assert(std::ranges::max( std::vector&amp;lt;f32&amp;gt;({f32::NAN, 3.f, 2.f})) == 3.f); // At run time, different answers. 3 is largest if it comes first. EXPECT_EQ(std::ranges::max( std::vector&amp;lt;f32&amp;gt;({2.f, 3.f, f32::NAN})), 3.f); // At run time, NAN is larger if it comes first. EXPECT_EQ(std::ranges::max( std::vector&amp;lt;f32&amp;gt;({f32::NAN, 3.f, 2.f})).is_nan(), true); // Undefined behaviour!!! EXPECT_EQ( std::ranges::max(std::vector&amp;lt;f32&amp;gt;()), 0.f); Those results are really not okay! They are the kind of thing that doesn’t show up in tests and then completely takes down production. Maybe introduces a security vuln. Here’s what I got with Subspace iterators. // Reproducible ordering. static_assert(sus::Vec&amp;lt;f32&amp;gt;::with(2.f, 3.f, 2.f) .into_iter() .max_by(&amp;amp;f32::total_cmp) == sus::some(3.f)); static_assert(sus::Vec&amp;lt;f32&amp;gt;::with(2.f, 3.f, f32::NAN) .into_iter() .max_by(&amp;amp;f32::total_cmp).unwrap().is_nan()); static_assert(sus::Vec&amp;lt;f32&amp;gt;::with(f32::NAN, 3.f, 2.f) .into_iter() .max_by(&amp;amp;f32::total_cmp).unwrap().is_nan()); // Same thing at runtime as at compile time. EXPECT_EQ(sus::Vec&amp;lt;f32&amp;gt;::with(2.f, 3.f, f32::NAN) .into_iter() .max_by(&amp;amp;f32::total_cmp).unwrap().is_nan(), true); EXPECT_EQ(sus::Vec&amp;lt;f32&amp;gt;::with(f32::NAN, 3.f, 2.f) .into_iter() .max_by(&amp;amp;f32::total_cmp).unwrap().is_nan(), true); // Defined behaviour. ^_^b static_assert(sus::Vec&amp;lt;f32&amp;gt;::with() .into_iter() .max_by(&amp;amp;f32::total_cmp) == sus::none()); Sure you can just write a total_cmp() method yourself and use that as the comparator with the standard ranges library. But you don’t have to, it compiles anyway so there’s nothing suggesting something is wrong. And then it returns garbage or corrupts your compilation with Undefined Behaviour. With Subspace total_cmp() is already there for you, and non-sense inputs don’t compile. This stuff is why Rust is pleasant to work in. C++ can be too.</summary></entry><entry><title type="html">Implementing Iterator::flatten() in C++ and in Rust</title><link href="http://orodu.net/2023/07/01/iterator-flatten.html" rel="alternate" type="text/html" title="Implementing Iterator::flatten() in C++ and in Rust" /><published>2023-07-01T00:00:00+00:00</published><updated>2023-07-01T00:00:00+00:00</updated><id>http://orodu.net/2023/07/01/iterator-flatten</id><content type="html" xml:base="http://orodu.net/2023/07/01/iterator-flatten.html">&lt;p&gt;You may have heard that C++ has concepts in version 20. You may have heard these compared to traits in Rust. Indeed you can do many of the same things with them. Today I found them to be a good demonstration of the complexity of writing C++ vs Rust.&lt;/p&gt;

&lt;p&gt;Here’s the &lt;a href=&quot;https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#method.flatten&quot;&gt;Iterator::flatten() method&lt;/a&gt; in Rust:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-1.png&quot; alt=&quot;The Rust Iterator::flatten() method&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The Items in the Iterator need to each be convertible to an Iterator as well. Then the resulting Iterator will return all the items from those Iterators. As in “Iterator[Iterator[i32]] =&amp;gt; Iterator[i32]”.&lt;/p&gt;

&lt;h2 id=&quot;intoiterator&quot;&gt;IntoIterator&lt;/h2&gt;

&lt;p&gt;IntoIterator is the trait that flatten() used, if it’s satisfied, the type can be converted to an Iterator &lt;a href=&quot;https://doc.rust-lang.org/stable/std/iter/trait.IntoIterator.html&quot;&gt;via calling into_iter()&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-2.png&quot; alt=&quot;The Rust IntoIterator trait&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The trait has an associated type called Item, which is what the created Iterator will return.&lt;/p&gt;

&lt;p&gt;It requires that the type returned by into_iter() actually implements Iterator by constraining it with the IntoIter rule.&lt;/p&gt;

&lt;h2 id=&quot;the-iteratorflatten-implementation&quot;&gt;The Iterator::flatten() implementation&lt;/h2&gt;

&lt;p&gt;The actual implementation of flatten() is &lt;a href=&quot;https://doc.rust-lang.org/stable/src/core/iter/traits/iterator.rs.html#1587-1590&quot;&gt;a single function call&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-3.png&quot; alt=&quot;The Rust Iterator::flatten() implementation&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Which creates a Flatten type that is the resulting iterator, the “IntoIter” type mentioned in the IntoIterator trait above.&lt;/p&gt;

&lt;h2 id=&quot;the-flatten-iterator-type&quot;&gt;The Flatten iterator type&lt;/h2&gt;

&lt;p&gt;The &lt;a href=&quot;https://doc.rust-lang.org/stable/std/iter/struct.Flatten.html&quot;&gt;Flatten type&lt;/a&gt; is also pretty simple. The implementation relies on a FlattenCompat abstraction to share implementation with flat_map():&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-4.png&quot; alt=&quot;The Rust Flatten type&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The where clause on Flatten points out once again that the Items in the outer Iterator can be converted to (or are) Iterators themselves.&lt;/p&gt;

&lt;h2 id=&quot;the-flatten-implementation&quot;&gt;The Flatten implementation&lt;/h2&gt;

&lt;p&gt;The Flatten::next() method, which is used to implement the Iterator trait &lt;a href=&quot;https://doc.rust-lang.org/stable/src/core/iter/adapters/flatten.rs.html#204-212&quot;&gt;returns a U::Item type&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-5.png&quot; alt=&quot;The Rust Flatten type's implementation of the Iterator trait&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The bound here requires that the Iterator we’re flattening has Items that satisfy IntoIterator, and we assign names to the associated types in IntoIterator.&lt;/p&gt;

&lt;p&gt;The U is a name we assign to the return type of the into_iter() method in the IntoIterator trait, as we see by the assignment to IntoIter. The Items from those inner iterators are U::Item.&lt;/p&gt;

&lt;p&gt;So now we’re going to return from the Flatten type’s Iterator implementation the inner types. This is not the most simplest of APIs but the type relationships are pretty straightforward.&lt;/p&gt;

&lt;p&gt;What does this look like in C++ Subspace?&lt;/p&gt;

&lt;h2 id=&quot;iteratorflatten-in-c&quot;&gt;Iterator::flatten() in C++&lt;/h2&gt;

&lt;p&gt;Here’s the C++ version of Iterator::flatten(). It’s in the IteratorBase class, not the Iterator concept. In Rust this goes directly in the trait, but you can’t add default methods to a C++ concept as they only provide boolean yes/no matching against types. So instead this lives on IteratorBase, which all Iterator types are required to inherit from (as final) in order to satisfy the Iterator concept.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-6.png&quot; alt=&quot;The C++ version of Iterator::flatten()&quot; /&gt;&lt;/p&gt;

&lt;p&gt;The method has a concept requirement, much like the Rust trait bound on IntoIterator. But it’s called IntoIteratorAny. Why the “Any” part?&lt;/p&gt;

&lt;h2 id=&quot;the-intoiterator-concept-in-c&quot;&gt;The IntoIterator concept in C++&lt;/h2&gt;

&lt;p&gt;We have an IntoIterator concept in C++ too. It has a type T which is the type that can be converted to an Iterator, and something like Rust’s associated type, Item, as a second template parameter.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-7.png&quot; alt=&quot;The C++ IntoIterator concept&quot; /&gt;&lt;/p&gt;

&lt;p&gt;If a type satisfies IntoIterator, then we know it can convert to an Iterator that returns Items through the into_iter() method. And the return type of into_iter() is constrained to satisfy the Iterator concept, like the Rust trait.&lt;/p&gt;

&lt;p&gt;The C++ complexity cracks start to show here. In the Rust generics and type system when you have a type you.. Well you have a type. But in C++ you have to worry about whether that type is const, or volatile, or an lvalue reference, or a const reference, or an rvalue reference! You do some 6D chess in your head to figure out just what type of thing you want in all of those situations, which you want to accept or reject.&lt;/p&gt;

&lt;p&gt;Here we don’t care about reference input types, we want to know that when we have an rvalue of the type, and we call into_iter() on it, we get an Iterator, so we need to use std::remove_cvref_t&lt;T&gt;. Hopefully that was the right choice, and not std::remove_const_t&amp;lt;std::remove_reference_t&lt;T&gt;&amp;gt; or some other configuration - it’s hard to ever be completely confident.&lt;/T&gt;&lt;/T&gt;&lt;/p&gt;

&lt;h2 id=&quot;getting-the-intoiterator-item-type&quot;&gt;Getting the IntoIterator Item type&lt;/h2&gt;

&lt;p&gt;But why didn’t we use IntoIterator on the flatten() method? Originally, I did that, as I’ve done with other methods, however for flatten() we don’t know apriori what the types are inside the inner Iterators.&lt;/p&gt;

&lt;p&gt;That was not a problem in the Rust flatten() method at all, it constrained Self::Item to IntoIterator and moved on. But to use the IntoIterator concept in C++ we need to pass two types, the type-to-be-converted as well as the Item. But the Item is not known here. We can figure it out though, by seeing what Iterator type gets returned from into_iter() and getting the Item type off of it:&lt;/p&gt;

&lt;p&gt;typename std::decay_t&amp;lt;decltype(std::declval&amp;lt;std::remove_cvref_t&lt;Item&gt;&amp;amp;&amp;amp;&amp;gt;().into_iter())&amp;gt;::Item&lt;/Item&gt;&lt;/p&gt;

&lt;p&gt;Lol. Since that’s not decipherable, here it is as a picture.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-8.png&quot; alt=&quot;The type traits to get the IntoIterator Item&quot; /&gt;&lt;/p&gt;

&lt;p&gt;But putting that in random code is horrifying.&lt;/p&gt;

&lt;h2 id=&quot;adding-the-intoiteratorany-concept-in-c&quot;&gt;Adding the IntoIteratorAny concept in C++&lt;/h2&gt;

&lt;p&gt;So to avoid writing inscrutable type traits on our flatten() method, we shove it behind another concept, IntoIteratorAny. It’s a concept that is satisfied for a type that can convert to an Iterator through the into_iter() method, without placing a bound on the Item type of the returned Iterator:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-9.png&quot; alt=&quot;The C++ IntoIteratorAny concept&quot; /&gt;&lt;/p&gt;

&lt;p&gt;We don’t need two traits to express this same thing in Rust, but I have not found a way to avoid it and keep the flatten() method concise in C++.&lt;/p&gt;

&lt;h2 id=&quot;the-body-of-iteratorflatten-in-c&quot;&gt;The body of Iterator::flatten() in C++&lt;/h2&gt;

&lt;p&gt;The body of the Rust flatten() method was simply Flatten::new(self). The C++ method has a bit more going on, all of which is hiding a ton of other machinery.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sized&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;SizedIteratorType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Iter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Sized alias is a template instantiation of SizedIterator, which allows us to type-erase Iterator types, and then nest Iterators inside each other.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flatten&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flatten&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;typename&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;IntoIteratorOutputType&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Item&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The sus::iter::IntoIteratorOutputType type alias is a helper used to figure out what Iterator type will be constructed from the call to into_iter() on a type that is IntoIterator. The Flatten type’s bounds in Rust allowed us to name that type U, as the trait has an associated type called IntoIter. Concepts in C++ don’t come with associated types, so you must use independent type inspection to get to it instead.&lt;/p&gt;

&lt;p&gt;Here’s the implementation of IntoIteratorOutputType:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-10.png&quot; alt=&quot;The IntoIteratorOutputType type alias&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note that the IntoIteratorAny concept and the IntoIteratorOutputType alias were handled entirely by the generic bounds of the IntoIterator trait in Rust in a concise and simple way. C++ required a lot more typing, a lot more complexity, a lot more room for mistakes. You can express more too, but at what cost.&lt;/p&gt;

&lt;h2 id=&quot;the-c-flatten-iterator-type&quot;&gt;The C++ Flatten Iterator type&lt;/h2&gt;

&lt;p&gt;Last, the method call to Flatten constructs the Flatten type which is the returned Iterator. We move “this” into the Flatten type by converting it into the SizedIterator.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Flatten&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;make_sized_iterator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;static_cast&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Iter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The Flatten type is a class that subclasses InteratorBase, like we mentioned before. It’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[[nodiscard]]&lt;/code&gt;, which Rust achieves by putting the same in a single place, &lt;a href=&quot;https://doc.rust-lang.org/stable/src/core/iter/traits/iterator.rs.html#72&quot;&gt;on the Iterator trait&lt;/a&gt;. The “InnerSizedIter” is like the “I” type on the Rust version of Flatten. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;[[sus_trivial_abi]]&lt;/code&gt; attribute marks the class as “clang::trivial_abi” if your compiler is Clang, which generates a warning on other compilers so has to go behind a macro.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-11.png&quot; alt=&quot;The C++ Flatten class&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;conclusion-and-iterator-concept&quot;&gt;Conclusion and Iterator concept&lt;/h2&gt;

&lt;p&gt;And with that we have the tools to write Iterator::flatten() in Rust or in C++ with Subspace.&lt;/p&gt;

&lt;p&gt;For the interested, here’s the Iterator C++ concept, which has its fair share of worrying about const, references, and lvalue/rvalue-state embedded in the types:&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-07-01-iterator-flatten/flatten-12.png&quot; alt=&quot;The C++ Iterator concept&quot; /&gt;&lt;/p&gt;</content><author><name>Dana Jansens</name></author><category term="Subspace" /><category term="C++" /><category term="Rust" /><summary type="html">You may have heard that C++ has concepts in version 20. You may have heard these compared to traits in Rust. Indeed you can do many of the same things with them. Today I found them to be a good demonstration of the complexity of writing C++ vs Rust. Here’s the Iterator::flatten() method in Rust: The Items in the Iterator need to each be convertible to an Iterator as well. Then the resulting Iterator will return all the items from those Iterators. As in “Iterator[Iterator[i32]] =&amp;gt; Iterator[i32]”. IntoIterator IntoIterator is the trait that flatten() used, if it’s satisfied, the type can be converted to an Iterator via calling into_iter(): The trait has an associated type called Item, which is what the created Iterator will return. It requires that the type returned by into_iter() actually implements Iterator by constraining it with the IntoIter rule. The Iterator::flatten() implementation The actual implementation of flatten() is a single function call: Which creates a Flatten type that is the resulting iterator, the “IntoIter” type mentioned in the IntoIterator trait above. The Flatten iterator type The Flatten type is also pretty simple. The implementation relies on a FlattenCompat abstraction to share implementation with flat_map(): The where clause on Flatten points out once again that the Items in the outer Iterator can be converted to (or are) Iterators themselves. The Flatten implementation The Flatten::next() method, which is used to implement the Iterator trait returns a U::Item type: The bound here requires that the Iterator we’re flattening has Items that satisfy IntoIterator, and we assign names to the associated types in IntoIterator. The U is a name we assign to the return type of the into_iter() method in the IntoIterator trait, as we see by the assignment to IntoIter. The Items from those inner iterators are U::Item. So now we’re going to return from the Flatten type’s Iterator implementation the inner types. This is not the most simplest of APIs but the type relationships are pretty straightforward. What does this look like in C++ Subspace? Iterator::flatten() in C++ Here’s the C++ version of Iterator::flatten(). It’s in the IteratorBase class, not the Iterator concept. In Rust this goes directly in the trait, but you can’t add default methods to a C++ concept as they only provide boolean yes/no matching against types. So instead this lives on IteratorBase, which all Iterator types are required to inherit from (as final) in order to satisfy the Iterator concept. The method has a concept requirement, much like the Rust trait bound on IntoIterator. But it’s called IntoIteratorAny. Why the “Any” part? The IntoIterator concept in C++ We have an IntoIterator concept in C++ too. It has a type T which is the type that can be converted to an Iterator, and something like Rust’s associated type, Item, as a second template parameter. If a type satisfies IntoIterator, then we know it can convert to an Iterator that returns Items through the into_iter() method. And the return type of into_iter() is constrained to satisfy the Iterator concept, like the Rust trait. The C++ complexity cracks start to show here. In the Rust generics and type system when you have a type you.. Well you have a type. But in C++ you have to worry about whether that type is const, or volatile, or an lvalue reference, or a const reference, or an rvalue reference! You do some 6D chess in your head to figure out just what type of thing you want in all of those situations, which you want to accept or reject. Here we don’t care about reference input types, we want to know that when we have an rvalue of the type, and we call into_iter() on it, we get an Iterator, so we need to use std::remove_cvref_t. Hopefully that was the right choice, and not std::remove_const_t&amp;lt;std::remove_reference_t&amp;gt; or some other configuration - it’s hard to ever be completely confident. Getting the IntoIterator Item type But why didn’t we use IntoIterator on the flatten() method? Originally, I did that, as I’ve done with other methods, however for flatten() we don’t know apriori what the types are inside the inner Iterators. That was not a problem in the Rust flatten() method at all, it constrained Self::Item to IntoIterator and moved on. But to use the IntoIterator concept in C++ we need to pass two types, the type-to-be-converted as well as the Item. But the Item is not known here. We can figure it out though, by seeing what Iterator type gets returned from into_iter() and getting the Item type off of it: typename std::decay_t&amp;lt;decltype(std::declval&amp;lt;std::remove_cvref_t&amp;amp;&amp;amp;&amp;gt;().into_iter())&amp;gt;::Item Lol. Since that’s not decipherable, here it is as a picture. But putting that in random code is horrifying. Adding the IntoIteratorAny concept in C++ So to avoid writing inscrutable type traits on our flatten() method, we shove it behind another concept, IntoIteratorAny. It’s a concept that is satisfied for a type that can convert to an Iterator through the into_iter() method, without placing a bound on the Item type of the returned Iterator: We don’t need two traits to express this same thing in Rust, but I have not found a way to avoid it and keep the flatten() method concise in C++. The body of Iterator::flatten() in C++ The body of the Rust flatten() method was simply Flatten::new(self). The C++ method has a bit more going on, all of which is hiding a ton of other machinery. using Sized = SizedIteratorType&amp;lt;Iter&amp;gt;::type; The Sized alias is a template instantiation of SizedIterator, which allows us to type-erase Iterator types, and then nest Iterators inside each other. using Flatten = Flatten&amp;lt;typename IntoIteratorOutputType&amp;lt;Item&amp;gt;::Item, Sized&amp;gt;; The sus::iter::IntoIteratorOutputType type alias is a helper used to figure out what Iterator type will be constructed from the call to into_iter() on a type that is IntoIterator. The Flatten type’s bounds in Rust allowed us to name that type U, as the trait has an associated type called IntoIter. Concepts in C++ don’t come with associated types, so you must use independent type inspection to get to it instead. Here’s the implementation of IntoIteratorOutputType: Note that the IntoIteratorAny concept and the IntoIteratorOutputType alias were handled entirely by the generic bounds of the IntoIterator trait in Rust in a concise and simple way. C++ required a lot more typing, a lot more complexity, a lot more room for mistakes. You can express more too, but at what cost. The C++ Flatten Iterator type Last, the method call to Flatten constructs the Flatten type which is the returned Iterator. We move “this” into the Flatten type by converting it into the SizedIterator. return Flatten::with(make_sized_iterator(static_cast&amp;lt;Iter&amp;amp;&amp;amp;&amp;gt;(*this))); The Flatten type is a class that subclasses InteratorBase, like we mentioned before. It’s [[nodiscard]], which Rust achieves by putting the same in a single place, on the Iterator trait. The “InnerSizedIter” is like the “I” type on the Rust version of Flatten. The [[sus_trivial_abi]] attribute marks the class as “clang::trivial_abi” if your compiler is Clang, which generates a warning on other compilers so has to go behind a macro. Conclusion and Iterator concept And with that we have the tools to write Iterator::flatten() in Rust or in C++ with Subspace. For the interested, here’s the Iterator C++ concept, which has its fair share of worrying about const, references, and lvalue/rvalue-state embedded in the types:</summary></entry><entry><title type="html">Bounds Safety: Avoiding Death by a Thousand Constructors</title><link href="http://orodu.net/2023/06/05/not-generating-constructors.html" rel="alternate" type="text/html" title="Bounds Safety: Avoiding Death by a Thousand Constructors" /><published>2023-06-05T00:00:00+00:00</published><updated>2023-06-05T00:00:00+00:00</updated><id>http://orodu.net/2023/06/05/not-generating-constructors</id><content type="html" xml:base="http://orodu.net/2023/06/05/not-generating-constructors.html">&lt;p&gt;&lt;strong&gt;tl;dr We are going to see how Subspace types allow a zero-cost transition from native pointers to
bounded view types, which enable spatial memory safety, and that can’t be achieved with the
standard library types.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Long ago, C and C++ programmers passed around pointers to arrays, hopefully with a size to indicate
how large the array was in order to avoid reading/writing out of bounds, though frequently not. This
is still a common pattern, though “modern C++” has provided tools to eliminate this class of memory
safety bug, through the use of “view” types. These are types like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::string_view&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt;
in the standard library. Unfortunately, these types don’t actually check for using memory out of
bounds in the C++ standard.&lt;/p&gt;

&lt;p&gt;Much has been written about the memory unsafety of lack of bounds checking, and we (as in the
software industry) have known ways to solve this simple issue for decades now.&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Here’s an article from 1996 (almost 30 years ago)
&lt;a href=&quot;https://ecs.syr.edu/faculty/fawcett/Handouts/cse775/presentations/BruceMcKinneyPapers/safeArrays.htm&quot;&gt;proposing a bounds-checked structure&lt;/a&gt;
for interacting with
&lt;a href=&quot;https://learn.microsoft.com/en-us/cpp/mfc/ole-background&quot;&gt;OLE&lt;/a&gt;.&lt;/li&gt;
  &lt;li&gt;Here’s &lt;a href=&quot;https://upload.wikimedia.org/wikipedia/commons/5/50/C%2B%2B_in_safety_critical_systems_%28IA_cinsafetycritica5769bink%29.pdf&quot;&gt;a NIST paper titled “C++ in Safety Critical Systems”&lt;/a&gt;
from 1995 which states “Class libraries that provide smart (safe) pointers and array bounds
checking should be used.”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In 2022, The Clang/Libc++ project accepted the
&lt;a href=&quot;https://discourse.llvm.org/t/rfc-c-buffer-hardening/65734&quot;&gt;C++ Buffer Hardening&lt;/a&gt; proposal that
makes it possible to enable hardening checks inside these standard library types with bounds -
though it requires you to compile the standard library yourself with the option enabled.
The compiled copy of the standard library that shipped with your operating system likely
&lt;a href=&quot;https://godbolt.org/z/Y4n99dx5j&quot;&gt;doesn’t have this enabled&lt;/a&gt;. At least your
&lt;a href=&quot;https://bugs.chromium.org/p/chromium/issues/detail?id=1335422&quot;&gt;web browser&lt;/a&gt; may have this enabled
(Chrome has since Q1 2023, but I don’t know about any other browser,
&lt;a href=&quot;https://chromium.googlesource.com/chromium/src/+/master/docs/security/faq.md#how-can-i-know-which-fixes-to-include-in-my-downstream-project&quot;&gt;Chromium-derived or not&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;The same proposal introduces the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;-Wunsafe-buffer-usage&lt;/code&gt; &lt;a href=&quot;https://clang.llvm.org/docs/DiagnosticsReference.html#wunsafe-buffer-usage&quot;&gt;warning in Clang&lt;/a&gt;
which you can enable to ban pointer arithmetic. This is very useful to help a
project convert all their native array pointers into view types, which pair a size 
with the pointer through the type system. See, the problem is that even if you enable bounds
checking in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::string_view&lt;/code&gt;, the majority of C++ code really doesn’t use those
types. The large C++ codebases that power modern technology are still full of pointers to arrays.
So this warning gives a mechanism to start migrating away from pointers,
&lt;em&gt;and then prevent backsliding&lt;/em&gt;, working toward banning pointer arithmetic entirely outside of a
few exceptional types.&lt;/p&gt;

&lt;h2 id=&quot;performance-implications&quot;&gt;Performance implications&lt;/h2&gt;

&lt;p&gt;Introducing memory safety into C++ codebases always comes with some performance degradation, because
there’s little you can check at compile time. So it means checking for bad program states when the
program is running. These performance changes are not the kind you as a user will see or
feel, but the kind that shows up on benchmarks and that software teams then have many
&lt;a href=&quot;https://groups.google.com/a/chromium.org/g/platform-architecture-dev/c/hdMBHEYZLMA&quot;&gt;discussions about&lt;/a&gt;.
And these often end up blocking meaningful improvements to the safety of users and their data.&lt;/p&gt;

&lt;p&gt;In bounds checking, the compiler can see the values involved in the comparisons, as they come from
constants or other values related to the array. For instance when iterating through a span, stopping
before the span’s length, the compiler can see that every index into the span is less than its
length fairly trivially. This usually means the compiler removes the bounds checks. So no runtime
overhead, yay!&lt;/p&gt;

&lt;p&gt;But there’s another type of overhead that comes not from the bounds checks in view types like
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::string_view&lt;/code&gt;, but from the change to using those types at all.&lt;/p&gt;

&lt;p&gt;Here’s a commit that converts a bunch of functions to use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base::StringPiece&lt;/code&gt;, the Chromium string
view type which is much like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::string_view&lt;/code&gt;:
&lt;a href=&quot;https://chromium-review.googlesource.com/c/chromium/src/+/4575826&quot;&gt;Make histogram functions take base::StringPiece&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The functions were previously overloaded to take &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const std::string&amp;amp;&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const char*&lt;/code&gt;. The
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const char*&lt;/code&gt; overload presumes that:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;the string being passed in is null-terminated, and&lt;/li&gt;
  &lt;li&gt;the function should be consuming the full string up to the null character.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These assumptions are easily wrong when working with view types that encode a bounded view of an
array. Calling these functions requires converting that bounded string view into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::string&lt;/code&gt;
which involves generating a constructor, and quite possibly a heap allocation. But
many callers instead convert their bounded string view into a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const char*&lt;/code&gt; through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.data()&lt;/code&gt;
instead, which is a bug. Props to &lt;a href=&quot;https://github.com/davidben&quot;&gt;@davidben&lt;/a&gt; for noticing this common
anti-pattern in Chromium and working to fix the many places it occurs.&lt;/p&gt;

&lt;p&gt;So it appears at first glance to be a win to change these functions to take a string view type, in
this case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base::StringPiece&lt;/code&gt;. For callers with a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::string&lt;/code&gt;, it will copy the pointer and length
into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base::StringPiece&lt;/code&gt;. For callers with a bounded string view type, they don’t need to throw
away the bounds or allocate on the heap. Yet this created a pretty large binary size regression for
doing almost nothing, and trying to eliminate heap allocations.&lt;/p&gt;

&lt;p&gt;Chromium is an interesting test bed because it is an &lt;em&gt;enormous&lt;/em&gt; codebase. So when you make a small
change, it can easily be amplified immensely. In this case, we discovered that all those calls to a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base::StringPiece&lt;/code&gt; constructor grow the binary by 36KB. For unknown reasons, the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base::StringPiece&lt;/code&gt; constructor gets inlined, while the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::string_view&lt;/code&gt; constructor does not, so
you get less binary size growth with that type (and we’ll look at marking &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;base::StringPiece&lt;/code&gt; with a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;noinline&lt;/code&gt; attribute).&lt;/p&gt;

&lt;p&gt;In Chromium, when a change impacts performance, we can often just look for where binary size grew
to track down where the regression lies. So we’ve seen binary size growth correlate well with
performance loss. In this case we see strictly more codegen at each call site, which can certainly
become visible at scale in performance testing.
So &lt;em&gt;this also suggests a performance degradation by moving from pointers to a bounded view type&lt;/em&gt;.
And herein lies the problem. A project may successfully flip libc++ hardening on for their
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; types without seeing much of a performance impact, because the majority of bounds
checks are elided. But then as they convert more pointers to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; to increase their
coverage of bounds safety, the use of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; may introduce a different kind of performance
(and binary size) regression.&lt;/p&gt;

&lt;p&gt;Here’s a code example (&lt;a href=&quot;https://godbolt.org/z/14Kq9GG3r&quot;&gt;in compiler explorer&lt;/a&gt;):&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;cp&quot;&gt;#include &amp;lt;stddef.h&amp;gt;
#include &amp;lt;vector&amp;gt;
#include &amp;lt;span&amp;gt;
#include &amp;lt;fmt/core.h&amp;gt;
&lt;/span&gt;
&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;take_vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;take_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;take_span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;take_vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;take_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;take_span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We will look at the resulting code gen unoptimized, to keep things simple. But the above commit
demonstrated the same principle under optimizations.&lt;/p&gt;

&lt;p&gt;Here’s the call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_vector()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;take_vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;This is three instructions. Grab the pointer, store it, and call the function. Nice.&lt;/p&gt;

&lt;p&gt;Here’s the call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_pointer()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;si&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ze&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rbx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rsi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rbx&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;take_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That’s a lot more instructions, as there’s two function calls first. Then store the two values and
make the call. This is why we had a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const std::string&amp;amp;&lt;/code&gt; overload for those functions. In the case
the caller already had a decomposed pointer/length, this would result in just something like the
last three instructions, which is as good as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_vector()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;And here’s the call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_span()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rsi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rdx&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18446744073709551615&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;48&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;40&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rdx&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rsi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;take_span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18446744073709551615&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Oof, that’s a lot more instructions than &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_vector()&lt;/code&gt;. Calling this function with a vector has
changed three instructions into ten, and that’s &lt;em&gt;without&lt;/em&gt; inlining the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; constructor.&lt;/p&gt;

&lt;p&gt;I guess it’s easy to see why the binary size changes when we start to receive &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; in
function parameters instead of a reference to the owning container.&lt;/p&gt;

&lt;p&gt;What if we had a pointer and length already, but we wanted to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_span()&lt;/code&gt; instead of
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_pointer()&lt;/code&gt;, is that any better? Something like this:&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;take_span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;({&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;That turns into the following when calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_span()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rcx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rsi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rcx&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18446744073709551615&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;unsigned&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;long&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;56&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rdx&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;     &lt;span class=&quot;nb&quot;&gt;rsi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;    &lt;span class=&quot;nv&quot;&gt;take_span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18446744073709551615&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It’s one more instruction to call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_span()&lt;/code&gt; when you have a pointer/length pair than to call
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_pointer()&lt;/code&gt; when you have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt;!&lt;/p&gt;

&lt;p&gt;Conversions can be costly for codegen in C++, which is why they should pretty much
&lt;a href=&quot;https://github.com/chromium/subspace/blob/714e0cec352c77968e3caa3bf78b3b1d83ea984a/PRINCIPLES.md?plain=1#L85-L86&quot;&gt;always be explicit&lt;/a&gt;,
even for (if not especially for) these small vocabulary types. I go further to take the point of
view that conversion constructors are harmful to understanding what code you are generating, even
if &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;explicit&lt;/code&gt;, and &lt;a href=&quot;https://github.com/chromium/subspace/blob/714e0cec352c77968e3caa3bf78b3b1d83ea984a/PRINCIPLES.md?plain=1#L60-L62&quot;&gt;static constructor methods are better&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;But anyway, what do we do? I want to stop out-of-bounds bugs in C++ code from constantly being
exploited. I want to stop the people who want to steal all of your private data. Silent and
persistent compromise, which allows theft of your past, present and future data, is not as rare or
as difficult as it should be, and that is due in part to memory unsafety bugs like out of bounds
memory accesses.&lt;/p&gt;

&lt;h2 id=&quot;making-the-conversion-free&quot;&gt;Making the conversion free&lt;/h2&gt;

&lt;p&gt;While walking my puppy today, it occurred to me that it
is possible to have that conversion to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector&lt;/code&gt;from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; be free; to generate the
same code when calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_span()&lt;/code&gt; as when calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_vector()&lt;/code&gt;. Well, no okay it’s not
possible with the standard library. But it is possible &lt;a href=&quot;https://github.com/chromium/subspace/pull/246&quot;&gt;with the Subspace library&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let’s expand our example a bit (&lt;a href=&quot;https://godbolt.org/z/rGe6jjYdE&quot;&gt;in compiler explorer&lt;/a&gt;):&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;take_vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;take_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;abort&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;take_span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;take_span_ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;span&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;take_vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;take_slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Slice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;take_slice_ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Slice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;fmt&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;println&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;take_vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;take_pointer&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;take_span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;take_span_ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;take_vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;take_slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;take_slice_ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;b&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;We’ve added a call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_span_ref()&lt;/code&gt; which receives a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const std::span&amp;amp;&lt;/code&gt;. Unsurprisingly this
does not improve anything, here is the codegen for that call:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x60&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rsi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rdx&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;b2e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18446744073709551615&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;403&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;acf&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;take_span_ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18446744073709551615&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We have to construct a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; from the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::vector&lt;/code&gt; in order to give a reference to it in the
function call, so this comes out at eight instructions, without the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; constructor being
inlined.&lt;/p&gt;

&lt;p&gt;Now let’s look at the Subspace types.&lt;/p&gt;

&lt;p&gt;First, receiving a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const sus::Vec&amp;amp;&lt;/code&gt; looks the same as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const std::vector&amp;amp;&lt;/code&gt;, which isn’t surprising:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x70&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;403&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;c75&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;take_vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We grab the pointer, store it and call the function, for three instructions.&lt;/p&gt;

&lt;p&gt;And receiving a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Slice&lt;/code&gt; looks much like receiving a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x70&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;bf0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Slice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rdx&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rsi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;403&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;e1b&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;take_slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Slice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;We construct a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Slice&lt;/code&gt; and then pass that by value to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_slice()&lt;/code&gt;. This actually comes in
two instructions shorter than the call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_span()&lt;/code&gt;, but it’s still way more codegen than
passing the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const sus::Vec&amp;amp;&lt;/code&gt; directly.&lt;/p&gt;

&lt;p&gt;But lastly, we have the call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_slice_ref()&lt;/code&gt;, which now differs significantly from the
equivalent &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_span_ref()&lt;/code&gt; with the standard library types:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x70&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;404&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;bf0&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Slice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rax&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;403&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;fd1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;take_slice_ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Slice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Now we’re down to five instructions. We call the conversion operator to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Slice&amp;amp;&lt;/code&gt;, which returns
&lt;em&gt;a pointer&lt;/em&gt; that we store, and then call &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_slice_ref()&lt;/code&gt; with it.&lt;/p&gt;

&lt;p&gt;But why is this operator returning &lt;em&gt;a pointer&lt;/em&gt;, instead of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Slice&lt;/code&gt; by value? The trick here
is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Vec&lt;/code&gt; is implemented by holding a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::SliceMut&lt;/code&gt; inside it, which holds a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Slice&lt;/code&gt; inside it&lt;sup id=&quot;fnref:1&quot; role=&quot;doc-noteref&quot;&gt;&lt;a href=&quot;#fn:1&quot; class=&quot;footnote&quot; rel=&quot;footnote&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;, which is where the pointer/length pair is finally found. This means that
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; can convert to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&lt;/code&gt; without any constructor being invoked.&lt;/p&gt;

&lt;h3 id=&quot;with-optimizations&quot;&gt;With optimizations&lt;/h3&gt;

&lt;p&gt;Ok let’s turn some optimizations back on and see what we get.&lt;/p&gt;

&lt;p&gt;Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_vector(const std::vector&amp;amp;)&lt;/code&gt; grabs the pointer and invokes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;call&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rsp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;4035&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;e6&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;take_vector&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;vector&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;al&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;locator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_span(std::span)&lt;/code&gt; inlined the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; constructor for us, which sets its two data
members and then invokes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;call&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbx&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;esi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x7&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;403719&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;take_span&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18446744073709551615&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_span_ref(const std::span&amp;amp;)&lt;/code&gt; is worse, as it constructs a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; inline, then has
to take its address. No doubt that’s where we get the guidance from to pass it by value:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rsp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rbx&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rsp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x38&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x7&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;lea&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rsp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;4037&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ab&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;take_span_ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;sp&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;an&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;18446744073709551615&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;ul&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_vec(const sus::Vec&amp;amp;)&lt;/code&gt; looks similar to receiving &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const std::vector&amp;amp;&lt;/code&gt;, as it grabs
the pointer and invokes &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;call&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rsp&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;403840&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;take_vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_slice(sus::Slice)&lt;/code&gt; looks similar to receiving &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt;, inlining the construction
of a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Slice&lt;/code&gt;, then passing it by value:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rsp&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rsi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;QWORD&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;PTR&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rsp&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;mh&quot;&gt;0x8&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;4038&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;e3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;take_slice&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Slice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_slice_ref(const sus::Slice&amp;amp;)&lt;/code&gt; is where things differ in a meaningful way:&lt;/p&gt;
&lt;div class=&quot;language-nasm highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nf&quot;&gt;mov&lt;/span&gt;    &lt;span class=&quot;nb&quot;&gt;rdi&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;rsp&lt;/span&gt;
&lt;span class=&quot;nf&quot;&gt;call&lt;/span&gt;   &lt;span class=&quot;mi&quot;&gt;403981&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;take_slice_ref&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;Slice&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;const&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_slice_ref()&lt;/code&gt; uses the exact same instructions as the call to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;take_vec()&lt;/code&gt;. &lt;em&gt;This
means we are able to use a bounded view type (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Slice&lt;/code&gt;) as a function parameter without taking
a binary size and performance cost at every function call that moves from an owning type to a view
type&lt;/em&gt;. And that’s something that we can’t do with the standard library types.&lt;/p&gt;

&lt;h3 id=&quot;benchmarks&quot;&gt;Benchmarks&lt;/h3&gt;

&lt;p&gt;Here’s all of the options &lt;a href=&quot;https://quick-bench.com/q/hmMT9RAlKTY4YhHJ9pG8_-lw9wA&quot;&gt;in Quickbench, compiled with GCC&lt;/a&gt;.
We call the function once per iteration, and the function does a single indexing operation into
each slice. The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const sus::Slice&amp;lt;T&amp;gt;&amp;amp;&lt;/code&gt; option comes out on top, exactly equal with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const sus::Vec&amp;lt;T&amp;gt;&amp;amp;&lt;/code&gt; which aligns with what we see in the assembly code above.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-06-05-not-generating-constructors/quickbench-slices-gcc.png&quot; alt=&quot;QuickBench results of GCC 12.2 with -O3&quot; /&gt;&lt;/p&gt;

&lt;p&gt;Note: We don’t call the function a bunch of times, like we do with Clang, because GCC ends up
setting up the stack for the function call once and then just doing &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;call&lt;/code&gt; successively, which is
not representative of the whole function call operation.&lt;/p&gt;

&lt;p&gt;And here’s the same thing &lt;a href=&quot;https://quick-bench.com/q/jZe_CSs6yivoWUenVtkFSYAt3AA&quot;&gt;in Quickbench, but compiled with Clang 15&lt;/a&gt;. We call the function 21 times per iteration
to avoid Clang mostly measuring the benchmark harness itself, and the function again does a single
indexing operation into each slice.&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/resources/2023-06-05-not-generating-constructors/quickbench-slices-clang.png&quot; alt=&quot;QuickBench results of Clang 15 with -O3&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;benchmarks-result&quot;&gt;Benchmarks Result&lt;/h3&gt;

&lt;p&gt;Across both compilers, we can see that receiving a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const sus::Slice&amp;amp;&lt;/code&gt; parameter from an owning
vector argument is as efficient as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const sus::Vec&amp;amp;&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const std::vector&amp;amp;&lt;/code&gt;, while all three are
more efficient than receiving &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt;.&lt;/p&gt;

&lt;h2 id=&quot;library-choices-that-got-us-here&quot;&gt;Library choices that got us here&lt;/h2&gt;

&lt;p&gt;There’s a couple differences in Subspace that work together to get us to this point.&lt;/p&gt;

&lt;p&gt;First, the nesting strategy of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec{SliceMut{Slice{T*}}}&lt;/code&gt;. This is not done with inheritance as you
might first expect. That would allow a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const Vec&amp;amp;&lt;/code&gt; to be used as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const Slice&amp;amp;&lt;/code&gt;, but it would
also allow a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const Vec&amp;amp;&lt;/code&gt; to be used as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const SliceMut&amp;amp;&lt;/code&gt;. A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt; provides a mutable view
of the array, meaning this would be a backdoor through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const Vec&amp;amp;&lt;/code&gt; to trivially mutate the data
inside it. So instead &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt; provide operators to convert to references of their
nested types which provide the library control over when the types should be allowed to convert.
This ended up very similar in implementation to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Deref&lt;/code&gt; &lt;a href=&quot;https://doc.rust-lang.org/stable/std/ops/trait.Deref.html&quot;&gt;trait in Rust&lt;/a&gt;, which is what inspired me to try this
at all.&lt;/p&gt;

&lt;p&gt;Secondly, the splitting of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt; into different types. Originally I had designed
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&lt;/code&gt; to specify the const-ness of its view through the template parameter, like you would with
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt;. That is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&amp;lt;const T&amp;gt;&lt;/code&gt; was a const view and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&amp;lt;T&amp;gt;&lt;/code&gt; was mutable. This made hiding
methods that should not exist on a const view much more annoying, as they would need to each grow
a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requires(!std::is_const_v&amp;lt;T&amp;gt;)&lt;/code&gt;. For programmers reading the API these are purely noise when you
have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&amp;lt;const T&amp;gt;&lt;/code&gt;, making the API harder to read and use and hack on. I think the standard
library type gets away with this by putting almost no methods on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt;, as it has a
methodology of providing free functions (like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::find()&lt;/code&gt;) instead. This type-splitting means that
it’s much simpler to have a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const SliceMut&amp;lt;T&amp;gt;&amp;amp;&lt;/code&gt; still implement a mutable view of the array.
A &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const sus::Slice&amp;lt;T&amp;gt;&lt;/code&gt; would have been more deceptive.&lt;/p&gt;

&lt;p&gt;Since the standard library differs on both of these, I don’t think the same strategy would work as
well in that environment, even if standard was willing to break backward compatibility to get this
performance win.&lt;/p&gt;

&lt;h3 id=&quot;thanks&quot;&gt;Thanks&lt;/h3&gt;

&lt;p&gt;Thank you &lt;a href=&quot;https://github.com/noncombatant&quot;&gt;@noncombatant&lt;/a&gt; for the excellent proofreading and
suggestions for this post.&lt;/p&gt;

&lt;div class=&quot;footnotes&quot; role=&quot;doc-endnotes&quot;&gt;
  &lt;ol&gt;
    &lt;li id=&quot;fn:1&quot; role=&quot;doc-endnote&quot;&gt;
      &lt;p&gt;For &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; to convert to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt; in this way, it must hold a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt; inside for &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; to return a reference to the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt;. Likewise, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt; can convert to a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&lt;/code&gt; since gaining &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const&lt;/code&gt; is a valid operation, though not the inverse. Thus &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt; contains a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&lt;/code&gt; and can convert to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&lt;/code&gt; by returning a reference to it. &lt;a href=&quot;#fnref:1&quot; class=&quot;reversefootnote&quot; role=&quot;doc-backlink&quot;&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;</content><author><name>Dana Jansens</name></author><category term="Subspace" /><category term="C++" /><category term="Performance" /><category term="Memory safety" /><summary type="html">tl;dr We are going to see how Subspace types allow a zero-cost transition from native pointers to bounded view types, which enable spatial memory safety, and that can’t be achieved with the standard library types. Long ago, C and C++ programmers passed around pointers to arrays, hopefully with a size to indicate how large the array was in order to avoid reading/writing out of bounds, though frequently not. This is still a common pattern, though “modern C++” has provided tools to eliminate this class of memory safety bug, through the use of “view” types. These are types like std::string_view or std::span in the standard library. Unfortunately, these types don’t actually check for using memory out of bounds in the C++ standard. Much has been written about the memory unsafety of lack of bounds checking, and we (as in the software industry) have known ways to solve this simple issue for decades now. Here’s an article from 1996 (almost 30 years ago) proposing a bounds-checked structure for interacting with OLE. Here’s a NIST paper titled “C++ in Safety Critical Systems” from 1995 which states “Class libraries that provide smart (safe) pointers and array bounds checking should be used.” In 2022, The Clang/Libc++ project accepted the C++ Buffer Hardening proposal that makes it possible to enable hardening checks inside these standard library types with bounds - though it requires you to compile the standard library yourself with the option enabled. The compiled copy of the standard library that shipped with your operating system likely doesn’t have this enabled. At least your web browser may have this enabled (Chrome has since Q1 2023, but I don’t know about any other browser, Chromium-derived or not). The same proposal introduces the -Wunsafe-buffer-usage warning in Clang which you can enable to ban pointer arithmetic. This is very useful to help a project convert all their native array pointers into view types, which pair a size with the pointer through the type system. See, the problem is that even if you enable bounds checking in std::span and std::string_view, the majority of C++ code really doesn’t use those types. The large C++ codebases that power modern technology are still full of pointers to arrays. So this warning gives a mechanism to start migrating away from pointers, and then prevent backsliding, working toward banning pointer arithmetic entirely outside of a few exceptional types. Performance implications Introducing memory safety into C++ codebases always comes with some performance degradation, because there’s little you can check at compile time. So it means checking for bad program states when the program is running. These performance changes are not the kind you as a user will see or feel, but the kind that shows up on benchmarks and that software teams then have many discussions about. And these often end up blocking meaningful improvements to the safety of users and their data. In bounds checking, the compiler can see the values involved in the comparisons, as they come from constants or other values related to the array. For instance when iterating through a span, stopping before the span’s length, the compiler can see that every index into the span is less than its length fairly trivially. This usually means the compiler removes the bounds checks. So no runtime overhead, yay! But there’s another type of overhead that comes not from the bounds checks in view types like std::span or std::string_view, but from the change to using those types at all. Here’s a commit that converts a bunch of functions to use base::StringPiece, the Chromium string view type which is much like std::string_view: Make histogram functions take base::StringPiece. The functions were previously overloaded to take const std::string&amp;amp; or const char*. The const char* overload presumes that: the string being passed in is null-terminated, and the function should be consuming the full string up to the null character. These assumptions are easily wrong when working with view types that encode a bounded view of an array. Calling these functions requires converting that bounded string view into a std::string which involves generating a constructor, and quite possibly a heap allocation. But many callers instead convert their bounded string view into a const char* through .data() instead, which is a bug. Props to @davidben for noticing this common anti-pattern in Chromium and working to fix the many places it occurs. So it appears at first glance to be a win to change these functions to take a string view type, in this case base::StringPiece. For callers with a std::string, it will copy the pointer and length into the base::StringPiece. For callers with a bounded string view type, they don’t need to throw away the bounds or allocate on the heap. Yet this created a pretty large binary size regression for doing almost nothing, and trying to eliminate heap allocations. Chromium is an interesting test bed because it is an enormous codebase. So when you make a small change, it can easily be amplified immensely. In this case, we discovered that all those calls to a base::StringPiece constructor grow the binary by 36KB. For unknown reasons, the base::StringPiece constructor gets inlined, while the std::string_view constructor does not, so you get less binary size growth with that type (and we’ll look at marking base::StringPiece with a noinline attribute). In Chromium, when a change impacts performance, we can often just look for where binary size grew to track down where the regression lies. So we’ve seen binary size growth correlate well with performance loss. In this case we see strictly more codegen at each call site, which can certainly become visible at scale in performance testing. So this also suggests a performance degradation by moving from pointers to a bounded view type. And herein lies the problem. A project may successfully flip libc++ hardening on for their std::span types without seeing much of a performance impact, because the majority of bounds checks are elided. But then as they convert more pointers to std::span to increase their coverage of bounds safety, the use of std::span may introduce a different kind of performance (and binary size) regression. Here’s a code example (in compiler explorer): #include &amp;lt;stddef.h&amp;gt; #include &amp;lt;vector&amp;gt; #include &amp;lt;span&amp;gt; #include &amp;lt;fmt/core.h&amp;gt; void take_vector(const std::vector&amp;lt;int&amp;gt;&amp;amp; v) { fmt::println(&quot;{}&quot;, v[0]); } void take_pointer(const int* p, size_t s) { if (0 &amp;gt;= s) abort(); fmt::println(&quot;{}&quot;, p[0]); } void take_span(std::span&amp;lt;const int&amp;gt; s) { fmt::println(&quot;{}&quot;, s[0]); } int main() { std::vector&amp;lt;int&amp;gt; a = {1, 2, 3, 4, 5, 6, 7}; take_vector(a); take_pointer(a.data(), a.size()); take_span(a); } We will look at the resulting code gen unoptimized, to keep things simple. But the above commit demonstrated the same principle under optimizations. Here’s the call to take_vector(): lea rax, [rbp-80] mov rdi, rax call take_vector(std::vector&amp;lt;int, std::allocator&amp;lt;int&amp;gt; &amp;gt; const&amp;amp;) This is three instructions. Grab the pointer, store it, and call the function. Nice. Here’s the call to take_pointer(): lea rax, [rbp-80] mov rdi, rax call std::vector&amp;lt;int, std::allocator&amp;lt;int&amp;gt; &amp;gt;::size() const mov rbx, rax lea rax, [rbp-80] mov rdi, rax call std::vector&amp;lt;int, std::allocator&amp;lt;int&amp;gt; &amp;gt;::data() mov rsi, rbx mov rdi, rax call take_pointer(int const*, unsigned long) That’s a lot more instructions, as there’s two function calls first. Then store the two values and make the call. This is why we had a const std::string&amp;amp; overload for those functions. In the case the caller already had a decomposed pointer/length, this would result in just something like the last three instructions, which is as good as take_vector(). And here’s the call to take_span(): lea rdx, [rbp-80] lea rax, [rbp-48] mov rsi, rdx mov rdi, rax call std::span&amp;lt;int const, 18446744073709551615ul&amp;gt;::span&amp;lt;std::vector&amp;lt;int, std::allocator&amp;lt;int&amp;gt; &amp;gt;&amp;amp;&amp;gt;(std::vector&amp;lt;int, std::allocator&amp;lt;int&amp;gt; &amp;gt;&amp;amp;) mov rdx, QWORD PTR [rbp-48] mov rax, QWORD PTR [rbp-40] mov rdi, rdx mov rsi, rax call take_span(std::span&amp;lt;int const, 18446744073709551615ul&amp;gt;) Oof, that’s a lot more instructions than take_vector(). Calling this function with a vector has changed three instructions into ten, and that’s without inlining the std::span constructor. I guess it’s easy to see why the binary size changes when we start to receive std::span in function parameters instead of a reference to the owning container. What if we had a pointer and length already, but we wanted to call take_span() instead of take_pointer(), is that any better? Something like this: auto p = a.data(); auto s = a.size(); take_span({p, s}); That turns into the following when calling take_span(): mov rdx, QWORD PTR [rbp-32] mov rcx, QWORD PTR [rbp-24] lea rax, [rbp-64] mov rsi, rcx mov rdi, rax call std::span&amp;lt;int const, 18446744073709551615ul&amp;gt;::span&amp;lt;int*&amp;gt;(int*, unsigned long) mov rdx, QWORD PTR [rbp-64] mov rax, QWORD PTR [rbp-56] mov rdi, rdx mov rsi, rax call take_span(std::span&amp;lt;int const, 18446744073709551615ul&amp;gt;) It’s one more instruction to call take_span() when you have a pointer/length pair than to call take_pointer() when you have a std::span! Conversions can be costly for codegen in C++, which is why they should pretty much always be explicit, even for (if not especially for) these small vocabulary types. I go further to take the point of view that conversion constructors are harmful to understanding what code you are generating, even if explicit, and static constructor methods are better. But anyway, what do we do? I want to stop out-of-bounds bugs in C++ code from constantly being exploited. I want to stop the people who want to steal all of your private data. Silent and persistent compromise, which allows theft of your past, present and future data, is not as rare or as difficult as it should be, and that is due in part to memory unsafety bugs like out of bounds memory accesses. Making the conversion free While walking my puppy today, it occurred to me that it is possible to have that conversion to std::vectorfrom std::span be free; to generate the same code when calling take_span() as when calling take_vector(). Well, no okay it’s not possible with the standard library. But it is possible with the Subspace library. Let’s expand our example a bit (in compiler explorer): void take_vector(const std::vector&amp;lt;int&amp;gt;&amp;amp; v) { fmt::println(&quot;{}&quot;, v[0]); } void take_pointer(const int* p, size_t s) { if (0 &amp;gt;= s) abort(); fmt::println(&quot;{}&quot;, p[0]); } void take_span(std::span&amp;lt;const int&amp;gt; s) { fmt::println(&quot;{}&quot;, s[0]); } void take_span_ref(const std::span&amp;lt;const int&amp;gt;&amp;amp; s) { fmt::println(&quot;{}&quot;, s[0]); } void take_vec(const sus::Vec&amp;lt;int&amp;gt;&amp;amp; v) { fmt::println(&quot;{}&quot;, v[0]); } void take_slice(sus::Slice&amp;lt;int&amp;gt; s) { fmt::println(&quot;{}&quot;, s[0]); } void take_slice_ref(const sus::Slice&amp;lt;int&amp;gt;&amp;amp; s) { fmt::println(&quot;{}&quot;, s[0]); } int main() { std::vector&amp;lt;int&amp;gt; a = {1, 2, 3, 4, 5, 6, 7}; take_vector(a); take_pointer(a.data(), a.size()); take_span(a); take_span_ref(a); auto b = sus::Vec&amp;lt;int&amp;gt;::with_values(1, 2, 3, 4, 5, 6, 7); take_vec(b); take_slice(b); take_slice_ref(b); } We’ve added a call to take_span_ref() which receives a const std::span&amp;amp;. Unsurprisingly this does not improve anything, here is the codegen for that call: lea rdx,[rbp-0x60] lea rax,[rbp-0x30] mov rsi,rdx mov rdi,rax call 404b2e &amp;lt;std::span&amp;lt;int const, 18446744073709551615ul&amp;gt;::span&amp;lt;std::vector&amp;lt;int, std::allocator&amp;lt;int&amp;gt; &amp;gt;&amp;amp;&amp;gt;(std::vector&amp;lt;int, std::allocator&amp;lt;int&amp;gt; &amp;gt;&amp;amp;)&amp;gt; lea rax,[rbp-0x30] mov rdi,rax call 403acf &amp;lt;take_span_ref(std::span&amp;lt;int const, 18446744073709551615ul&amp;gt; const&amp;amp;)&amp;gt; We have to construct a std::span from the std::vector in order to give a reference to it in the function call, so this comes out at eight instructions, without the std::span constructor being inlined. Now let’s look at the Subspace types. First, receiving a const sus::Vec&amp;amp; looks the same as const std::vector&amp;amp;, which isn’t surprising: lea rax,[rbp-0x70] mov rdi,rax call 403c75 &amp;lt;take_vec(sus::Vec&amp;lt;int&amp;gt; const&amp;amp;)&amp;gt; We grab the pointer, store it and call the function, for three instructions. And receiving a sus::Slice looks much like receiving a std::span: lea rax,[rbp-0x70] mov rdi,rax call 404bf0 &amp;lt;sus::Vec&amp;lt;int&amp;gt;::operator sus::Slice&amp;lt;int&amp;gt;&amp;amp;() &amp;amp;&amp;gt; mov rdx,QWORD PTR [rax] mov rax,QWORD PTR [rax+0x8] mov rdi,rdx mov rsi,rax call 403e1b &amp;lt;take_slice(sus::Slice&amp;lt;int&amp;gt;)&amp;gt; We construct a sus::Slice and then pass that by value to take_slice(). This actually comes in two instructions shorter than the call to take_span(), but it’s still way more codegen than passing the const sus::Vec&amp;amp; directly. But lastly, we have the call to take_slice_ref(), which now differs significantly from the equivalent take_span_ref() with the standard library types: lea rax,[rbp-0x70] mov rdi,rax call 404bf0 &amp;lt;sus::Vec&amp;lt;int&amp;gt;::operator sus::Slice&amp;lt;int&amp;gt;&amp;amp;() &amp;amp;&amp;gt; mov rdi,rax call 403fd1 &amp;lt;take_slice_ref(sus::Slice&amp;lt;int&amp;gt; const&amp;amp;)&amp;gt; Now we’re down to five instructions. We call the conversion operator to sus::Slice&amp;amp;, which returns a pointer that we store, and then call take_slice_ref() with it. But why is this operator returning a pointer, instead of a sus::Slice by value? The trick here is that sus::Vec is implemented by holding a sus::SliceMut inside it, which holds a sus::Slice inside it1, which is where the pointer/length pair is finally found. This means that Vec can convert to a SliceMut and Slice without any constructor being invoked. With optimizations Ok let’s turn some optimizations back on and see what we get. Calling take_vector(const std::vector&amp;amp;) grabs the pointer and invokes call: lea rdi,[rsp+0x10] call 4035e6 &amp;lt;take_vector(std::vector&amp;lt;int, std::allocator&amp;lt;int&amp;gt; &amp;gt; const&amp;amp;)&amp;gt; Calling take_span(std::span) inlined the std::span constructor for us, which sets its two data members and then invokes call: mov rdi,rbx mov esi,0x7 call 403719 &amp;lt;take_span(std::span&amp;lt;int const, 18446744073709551615ul&amp;gt;)&amp;gt; Calling take_span_ref(const std::span&amp;amp;) is worse, as it constructs a std::span inline, then has to take its address. No doubt that’s where we get the guidance from to pass it by value: mov QWORD PTR [rsp+0x30],rbx mov QWORD PTR [rsp+0x38],0x7 lea rdi,[rsp+0x30] call 4037ab &amp;lt;take_span_ref(std::span&amp;lt;int const, 18446744073709551615ul&amp;gt; const&amp;amp;)&amp;gt; Calling take_vec(const sus::Vec&amp;amp;) looks similar to receiving const std::vector&amp;amp;, as it grabs the pointer and invokes call: mov rdi,rsp call 403840 &amp;lt;take_vec(sus::Vec&amp;lt;int&amp;gt; const&amp;amp;)&amp;gt; Calling take_slice(sus::Slice) looks similar to receiving std::span, inlining the construction of a sus::Slice, then passing it by value: mov rdi,QWORD PTR [rsp] mov rsi,QWORD PTR [rsp+0x8] call 4038e3 &amp;lt;take_slice(sus::Slice&amp;lt;int&amp;gt;)&amp;gt; Calling take_slice_ref(const sus::Slice&amp;amp;) is where things differ in a meaningful way: mov rdi,rsp call 403981 &amp;lt;take_slice_ref(sus::Slice&amp;lt;int&amp;gt; const&amp;amp;)&amp;gt; The call to take_slice_ref() uses the exact same instructions as the call to take_vec(). This means we are able to use a bounded view type (sus::Slice) as a function parameter without taking a binary size and performance cost at every function call that moves from an owning type to a view type. And that’s something that we can’t do with the standard library types. Benchmarks Here’s all of the options in Quickbench, compiled with GCC. We call the function once per iteration, and the function does a single indexing operation into each slice. The const sus::Slice&amp;lt;T&amp;gt;&amp;amp; option comes out on top, exactly equal with const sus::Vec&amp;lt;T&amp;gt;&amp;amp; which aligns with what we see in the assembly code above. Note: We don’t call the function a bunch of times, like we do with Clang, because GCC ends up setting up the stack for the function call once and then just doing call successively, which is not representative of the whole function call operation. And here’s the same thing in Quickbench, but compiled with Clang 15. We call the function 21 times per iteration to avoid Clang mostly measuring the benchmark harness itself, and the function again does a single indexing operation into each slice. Benchmarks Result Across both compilers, we can see that receiving a const sus::Slice&amp;amp; parameter from an owning vector argument is as efficient as const sus::Vec&amp;amp; or const std::vector&amp;amp;, while all three are more efficient than receiving std::span. Library choices that got us here There’s a couple differences in Subspace that work together to get us to this point. First, the nesting strategy of Vec{SliceMut{Slice{T*}}}. This is not done with inheritance as you might first expect. That would allow a const Vec&amp;amp; to be used as a const Slice&amp;amp;, but it would also allow a const Vec&amp;amp; to be used as a const SliceMut&amp;amp;. A SliceMut provides a mutable view of the array, meaning this would be a backdoor through const Vec&amp;amp; to trivially mutate the data inside it. So instead Vec and SliceMut provide operators to convert to references of their nested types which provide the library control over when the types should be allowed to convert. This ended up very similar in implementation to the Deref trait in Rust, which is what inspired me to try this at all. Secondly, the splitting of Slice and SliceMut into different types. Originally I had designed Slice to specify the const-ness of its view through the template parameter, like you would with std::span. That is Slice&amp;lt;const T&amp;gt; was a const view and Slice&amp;lt;T&amp;gt; was mutable. This made hiding methods that should not exist on a const view much more annoying, as they would need to each grow a requires(!std::is_const_v&amp;lt;T&amp;gt;). For programmers reading the API these are purely noise when you have a Slice&amp;lt;const T&amp;gt;, making the API harder to read and use and hack on. I think the standard library type gets away with this by putting almost no methods on std::span, as it has a methodology of providing free functions (like std::find()) instead. This type-splitting means that it’s much simpler to have a const SliceMut&amp;lt;T&amp;gt;&amp;amp; still implement a mutable view of the array. A const sus::Slice&amp;lt;T&amp;gt; would have been more deceptive. Since the standard library differs on both of these, I don’t think the same strategy would work as well in that environment, even if standard was willing to break backward compatibility to get this performance win. Thanks Thank you @noncombatant for the excellent proofreading and suggestions for this post. For Vec to convert to SliceMut in this way, it must hold a SliceMut inside for Vec to return a reference to the SliceMut. Likewise, SliceMut can convert to a Slice since gaining const is a valid operation, though not the inverse. Thus SliceMut contains a Slice and can convert to Slice by returning a reference to it. &amp;#8617;</summary></entry><entry><title type="html">Subspace Update: Slices and Vec, Generators, More Iterators, Std Ranges, Callable concepts</title><link href="http://orodu.net/2023/04/30/slices-generators.html" rel="alternate" type="text/html" title="Subspace Update: Slices and Vec, Generators, More Iterators, Std Ranges, Callable concepts" /><published>2023-04-30T00:00:00+00:00</published><updated>2023-04-30T00:00:00+00:00</updated><id>http://orodu.net/2023/04/30/slices-generators</id><content type="html" xml:base="http://orodu.net/2023/04/30/slices-generators.html">&lt;p&gt;I’ve spent a few months mostly working on an enormous branch, so Subspace hasn’t seen a lot of
changes on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;main&lt;/code&gt;. Today I merged a lot of stuff, which makes it a good time to mention some of the
new things.&lt;/p&gt;

&lt;h2 id=&quot;slices-and-vec&quot;&gt;Slices and Vec&lt;/h2&gt;

&lt;p&gt;Micro post: &lt;a href=&quot;https://sunny.garden/@blinkygal/110289198753682615&quot;&gt;https://sunny.garden/@blinkygal/110289198753682615&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Today in Subspace I finished and merged all the methods for &lt;a href=&quot;https://danakj.github.io/subspace-docs/sus-containers-Slice.html&quot;&gt;Slice&lt;/a&gt; and &lt;a href=&quot;https://danakj.github.io/subspace-docs/sus-containers-SliceMut.html&quot;&gt;SliceMut&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&amp;lt;T&amp;gt;&lt;/code&gt; is a const reference to a contiguous range of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;const T&lt;/code&gt; (like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&amp;lt;const T&amp;gt;&lt;/code&gt;),
while &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&amp;lt;T&amp;gt;&lt;/code&gt; is a mutable reference to a contiguous range of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt; (like a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&amp;lt;T&amp;gt;&lt;/code&gt;).
Why are these different types? First, this allows the default, shorter wording to be the safer and
preferred one: const is default. Secondly, it’s not possible to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sort()&lt;/code&gt; on a Slice, but if they
were one type, it would require all methods that require mutable access to the pointed-to range to
be qualified with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;requires (!std::is_const_v&amp;lt;T&amp;gt;)&lt;/code&gt;. The API gets harder to understand and to use as
a result. This way, the SliceMut type has methods that allow mutation, and Slice simply does not.&lt;/p&gt;

&lt;p&gt;There are 95 methods in all! I started working on these two months ago on March 3. The
Pull Request is here: &lt;a href=&quot;https://github.com/chromium/subspace/pull/218/&quot;&gt;https://github.com/chromium/subspace/pull/218/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I’m kinda relieved to finally get through them all. There’s lots of useful stuff in there though,
including:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;searching&lt;/li&gt;
  &lt;li&gt;sorting&lt;/li&gt;
  &lt;li&gt;splitting&lt;/li&gt;
  &lt;li&gt;sub-slicing with ranges (including range literals)&lt;/li&gt;
  &lt;li&gt;iterating (forward and reverse) on splits, chunks, and sliding windows&lt;/li&gt;
  &lt;li&gt;joining&lt;/li&gt;
  &lt;li&gt;filling or repeating&lt;/li&gt;
  &lt;li&gt;working with prefixes and suffixes&lt;/li&gt;
  &lt;li&gt;swapping&lt;/li&gt;
  &lt;li&gt;reordering&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These methods all appear on &lt;a href=&quot;https://danakj.github.io/subspace-docs/sus-containers-Vec.html&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;&lt;/a&gt;
as well, which is an owning Slice. In addition, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; is usable in all places that a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&lt;/code&gt; or
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt; (if the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt; is not const) would be usable (except as an rvalue, which would allow
references to escape from a temporary object).
And similarly &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt; is usuable anywhere a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&lt;/code&gt; would be.&lt;/p&gt;

&lt;p&gt;I am sure There’s lots of room for performance tuning, and some TODOs left in this regard. As
always &lt;em&gt;everything comes with unit tests&lt;/em&gt;, so making future changes can be done with confidence.&lt;/p&gt;

&lt;h1 id=&quot;range-literals&quot;&gt;Range literals&lt;/h1&gt;

&lt;p&gt;In the C++ standard library, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::span&lt;/code&gt; type allows subslicing through the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subspan(offset)&lt;/code&gt;
and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;subspan(offset, count)&lt;/code&gt; overloads (implemented as a default argument). Then,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s.subspan(offset)&lt;/code&gt; in C++ is equivalent to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;s[offset..]&lt;/code&gt; in Rust and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;s.subspan(offset, count)&lt;/code&gt; in
C++ is equivalent to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;s[offset..(offset + count)]&lt;/code&gt; in Rust.&lt;/p&gt;

&lt;p&gt;But Rust is more expressive here. Its &lt;a href=&quot;https://doc.rust-lang.org/stable/std/ops/trait.RangeBounds.html&quot;&gt;RangeBounds&lt;/a&gt; can describe any possible range
with or without a front edge or back edge. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;..&lt;/code&gt; is everything, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2..&lt;/code&gt; starts from 2 and goes to the
end, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;..5&lt;/code&gt; starts at the beginning and stops at 5 (excluding 5), and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2..5&lt;/code&gt; starts at 2 and ends at
5 (excluding 5). It even allows &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2..=5&lt;/code&gt; to include 5 in the bounds.&lt;/p&gt;

&lt;p&gt;Instead of providing a bunch of overloads for this type of expressivity, Subspace slices (and Vec)
can be subsliced using the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;operator[](sus::ops::RangeBounds&amp;lt;usize&amp;gt; auto)&lt;/code&gt; operator, which aborts if
given a range out of bounds, or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_range(sus::ops::RangeBounds&amp;lt;usize&amp;gt; auto)&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_range_mut(sus::ops::RangeBounds&amp;lt;usize&amp;gt; auto)&lt;/code&gt; which return a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::Option&lt;/code&gt; holding nothing
if out of bounds or a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Slice&lt;/code&gt; (or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;SliceMut&lt;/code&gt; respectively) otherwise.
The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RangeBounds&lt;/code&gt; concept is satisfied by a set of types provided in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::ops&lt;/code&gt; that specify
a start, an end, both, or neither:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::ops::Range&lt;/code&gt; has a start and end (exclusive).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::ops::RangeFrom&lt;/code&gt; has a start, with an unbounded end.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::ops::RangeTo&lt;/code&gt; has an end, with an unbounded start.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::ops::RangeFull&lt;/code&gt; represented an unbounded start and end.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For dynamic values, these can be constructed from numeric values as they are aggegrate types. For
constant values, a literal syntax is provided. C++ doesn’t let us have all the nice things, so we
can’t use the exact literal syntax from Rust, as C++ requires non-quoted literals to parse as
numbers even if you’re going to do the parsing yourself. But we get this:&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;3..7&quot;_r&lt;/code&gt; is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Range&amp;lt;usize&amp;gt;&lt;/code&gt; from 3 to 7 (exclusive).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;3..=7&quot;_r&lt;/code&gt; is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Range&amp;lt;usize&amp;gt;&lt;/code&gt; from 3 to 7 (inclusive).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;3..&quot;_r&lt;/code&gt; is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RangeFrom&amp;lt;usize&amp;gt;&lt;/code&gt; that starts at 3.&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;..7&quot;_r&lt;/code&gt; is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RangeTo&amp;lt;usize&amp;gt;&lt;/code&gt; that ends at 7 (exclusive).&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;..&quot;_r&lt;/code&gt; is a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;RangeFull&amp;lt;usize&amp;gt;&lt;/code&gt; that represents an unbounded range.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Range types can be modified to produce new ranges with the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.start_at()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.end_at()&lt;/code&gt; methods.
These change, or add, a start or end bound, changing the type of the object when a new bound is
added. For instance &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::sus::ops::RangeFull().start_at(3)&lt;/code&gt; makes a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;::sus::ops::RangeFrom(3)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Mixing literal values with dynamic values is possible as well with these methods, such as a range
that starts at 5, but ends at &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x&lt;/code&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;5..&quot;_r.end_at(x)&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Certainly this is nowhere as nice as Rust’s &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5..x&lt;/code&gt; syntax. Maybe C++ can give us that one day. But I
want to provide the tools to write literal ranges when code authors would benefit from doing so, and
the range type aggregate constructors are always available too.&lt;/p&gt;

&lt;p&gt;Bringing this back to slices, a Vec can produce a slice for any range using its &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;operator[]&lt;/code&gt;,
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_range()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;get_range_mut()&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// A SliceMut over [1, 2, 3, 4, 5].&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s2&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;3..&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// A SliceMut over [4, 5].&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;..3&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// A SliceMut over [1, 2, 3].&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s4&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;2..3&quot;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;];&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;// A SliceMut over [3].&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Lastly, for signed ranges, there’s also a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;_rs&lt;/code&gt; literal suffix that produces ranges over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isize&lt;/code&gt;
instead of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usize&lt;/code&gt;, such as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;3..12&quot;_rs&lt;/code&gt; to make &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::ops::Range&amp;lt;isize&amp;gt;(3, 12)&lt;/code&gt;. But slices always
work with ranges over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usize&lt;/code&gt;.&lt;/p&gt;

&lt;h1 id=&quot;iterators&quot;&gt;Iterators&lt;/h1&gt;

&lt;p&gt;While I was slogging through the slice methods, I also added some exciting iterator features.&lt;/p&gt;

&lt;h2 id=&quot;generator-functions&quot;&gt;Generator functions&lt;/h2&gt;

&lt;p&gt;Micro post: &lt;a href=&quot;https://sunny.garden/@blinkygal/110044818048764779&quot;&gt;https://sunny.garden/@blinkygal/110044818048764779&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It’s now possible to write an Iterator that will compose nicely with all the existing Iterator
methods, but as a single function, instead of having to write a whole type. This is thanks to C++
coroutines, as they allow writing a generator function to modify iteration.&lt;/p&gt;

&lt;p&gt;This provides a means to write “control flow” into an iteration, in the spirit of
&lt;a href=&quot;https://without.boats/blog/the-registers-of-rust/&quot;&gt;https://without.boats/blog/the-registers-of-rust/&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here’s the generator unit tests to demonstrate what I mean.&lt;/p&gt;

&lt;p&gt;Here the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x()&lt;/code&gt; function produces an iterator over the set &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1, 2, 3, 4&lt;/code&gt;. This is composed with the
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter()&lt;/code&gt; method that keeps only things in the range &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(1, 4)&lt;/code&gt; (exclusive). The result is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2, 3&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;TEST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IterGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ComposeFromGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[]()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Generator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;co_yield&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;co_yield&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;co_yield&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;co_yield&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// Generator is trivially relocatable, so no need to box() it.&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Iterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([](&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;a&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Here the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;x()&lt;/code&gt; function composes with an input iterator, and it does the same as the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter()&lt;/code&gt;
call above, discarding anything outside the range &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(1, 4)&lt;/code&gt; (exclusive). It is chained with an
iterator from a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&amp;lt;i32&amp;gt;&lt;/code&gt; that contains &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1, 2, 3, 4&lt;/code&gt; and again the result is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;2, 3&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;TEST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;IterGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ComposeIntoGenerator&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[](&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Iterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Generator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;co_yield&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Iterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;construct&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;generate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;next&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;enumerate&quot;&gt;Enumerate&lt;/h2&gt;

&lt;p&gt;Micro post: &lt;a href=&quot;https://sunny.garden/@blinkygal/110223458907555381&quot;&gt;https://sunny.garden/@blinkygal/110223458907555381&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I also have added &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterator::enumerate&lt;/code&gt;. Adding the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::iter::Enumerate&lt;/code&gt; type was fairly trivial,
but in order to iterate backwards as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::iter::DoubleEndedIterator&lt;/code&gt;, it needs to know the total
length of the iteration sequence. That is, the input iterator needs to also be a
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::iter::ExactSizeIterator&lt;/code&gt;. While these concepts were already present in the library, I had not
plumbed them through composing iterator types. So that is now done.&lt;/p&gt;

&lt;p&gt;This means, for example, calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;filter()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map()&lt;/code&gt; on a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DoubleEndedIterator&lt;/code&gt; will produce
another &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DoubleEndedIterator&lt;/code&gt;. And when possible, such as when calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rev()&lt;/code&gt; on an
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExactSizeIterator&lt;/code&gt;, the resulting reversed iterator will also be an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExactSizeIterator&lt;/code&gt;.
And in the case of &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enumerate()&lt;/code&gt;, if the input iterator was a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;DoubleEndedIerator&lt;/code&gt; and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ExactSizeIterator&lt;/code&gt;, the output iterator will also be.&lt;/p&gt;

&lt;p&gt;Notably, iterators over a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Vec&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Array&lt;/code&gt; or slice are double-ended and have an exact known size and
thus satisfy these traits. The same will be true for most&lt;/p&gt;

&lt;p&gt;Here’s an example where we have an iterator over &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1, 2, 3, 4, 5&lt;/code&gt;. It is double-ended and its exact
size is known. The iterator is reversed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;rev()&lt;/code&gt;, so it will output &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;5, 4, 3, 2, 1&lt;/code&gt;. Then it
is composed with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;enumerate()&lt;/code&gt; which means each step includes the position in the iteration
sequence.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;chars&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with_values&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;sc&quot;&gt;'a'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'b'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'c'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'d'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;sc&quot;&gt;'e'&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;chars&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;rev&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;enumerate&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// enumerate() makes an iterator over a Tuple of position and value.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;E&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Tuple&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;static_assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Iterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;decltype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// The output iterator of enumerate() is a DoubleSidedIterator and&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;// ExactSizeIterator.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;static_assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;DoubleEndedIterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;decltype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;static_assert&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ExactSizeIterator&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;decltype&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;E&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// The output the position paired with the reversed values.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;it&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// Prints:&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// (0, e)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// (1, d)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// (2, c)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// (3, b)&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// (4, a)&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;cerr&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;(&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;size_t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pos&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;, &quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;)&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Side note: In this example, the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;usize&lt;/code&gt; position is casted to a primitive to print, as I did not yet
decide on a path for &lt;a href=&quot;https://github.com/chromium/subspace/issues/235&quot;&gt;integrating with streams&lt;/a&gt; or
to otherwise print things.&lt;/p&gt;

&lt;h2 id=&quot;stdlib-ranges&quot;&gt;Stdlib Ranges&lt;/h2&gt;

&lt;p&gt;Subspace’s first major compatability hook with the C++ standard library was added, with conversions
from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::iter::Iterator&lt;/code&gt; types to C++ ranges.&lt;/p&gt;

&lt;p&gt;Calling &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.range()&lt;/code&gt; on any iterator will produce a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::ranges::range&lt;/code&gt; output, which satisfies the
&lt;a href=&quot;https://en.cppreference.com/w/cpp/ranges/input_range&quot;&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::ranges::input_range&lt;/code&gt;&lt;/a&gt; concept.&lt;/p&gt;

&lt;p&gt;Here are some unit tests that demonstrate the behaviour, showing that the output from &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;.range()&lt;/code&gt; is
usable as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::ranges::viewable_range&lt;/code&gt; and a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;std::ranges_input_range&lt;/code&gt;. In these examples, the
iterator owns the values as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vec&lt;/code&gt; is consumed to produce the iterator through &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;into_iter()&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;n&quot;&gt;TEST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompatRanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;ViewableRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// all() requires a `std::ranges::viewable_range`.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;all&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;());&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;view&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;TEST&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;CompatRanges&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;InputRange&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

  &lt;span class=&quot;c1&quot;&gt;// filter() requires a `std::ranges::input_range`.&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;ranges&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;views&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;move&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;vec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
                                           &lt;span class=&quot;p&quot;&gt;[](&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;i&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;e&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;EXPECT_EQ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h1 id=&quot;closurescallable-concepts&quot;&gt;Closures/Callable concepts&lt;/h1&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Fn&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FnMut&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FnOnce&lt;/code&gt; are now concepts, not concrete types, just as the same are &lt;a href=&quot;https://doc.rust-lang.org/stable/std/ops/trait.Fn.html&quot;&gt;traits in
Rust&lt;/a&gt;.
Most notably they manage to take a function signature syntax in their template arguments while
still producing useful errors when given a non-matching type. This was rather non-trivial as C++
concepts do not allow partial specialization, and referring to structs in a concept stops good error
propogation.&lt;/p&gt;

&lt;p&gt;Also the template arguments allow some simple pattern matching.
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus::fn::Fn&amp;lt;sus::fn::Anything(i32)&amp;gt; auto&lt;/code&gt; will match any const-callable thing that takes an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i32&lt;/code&gt;,
regardless of its return type. And &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sus:fn::Fn&amp;lt;sus::fn::NonVoid(i32)&amp;gt; auto&lt;/code&gt; will match any
const-callable thing that takes an &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i32&lt;/code&gt; input and produces some non-void output.&lt;/p&gt;

&lt;p&gt;I spent a week on &lt;a href=&quot;https://github.com/chromium/subspace/pull/220&quot;&gt;this rewrite&lt;/a&gt;, which also
introduced lightwight &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FnRef&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FnMutRef&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FnOnceRef&lt;/code&gt; that are concrete types satifying the
above concepts and which refer to a function pointer or callable object like a lambda without
owning any state. These types are super useful to receive a callable that will be used during a
function but not stored beyond the lifetime of the function. And they don’t require your function
to become a template, which will help keep down compile times.&lt;/p&gt;

&lt;p&gt;Most Subspace methods that receive a callable now use these “Ref” implementation types instead of
the concepts. Though limitations of type deduction still mean we need to use concept types in some
cases, where the callable’s type signature is templated. For example with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Option::map()&lt;/code&gt; where the
return type of the callable determines the output of the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;map()&lt;/code&gt; function, using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FnRef&amp;lt;R(T&amp;amp;&amp;amp;)&amp;gt;&lt;/code&gt;
means the compiler can not deduce the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;R&lt;/code&gt; type:&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;note: 'Option&amp;lt;R&amp;gt; Option&amp;lt;int&amp;gt;::map(sus::fn::FnRef&amp;lt;R(T &amp;amp;&amp;amp;),&amp;gt;) noexcept &amp;amp;&amp;amp;':
  could not deduce template argument for 'sus::fn::FnRef&amp;lt;R(T &amp;amp;&amp;amp;),&amp;gt;'
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The result is some pretty unweildly C++ concept bounds, and there’s a ton of space for C++ to
improve here in the future:&lt;/p&gt;
&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;  &lt;span class=&quot;k&quot;&gt;template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FnOnce&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;NonVoid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;MapFn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;...,&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;std&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;invoke_result_t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MapFn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&amp;gt;&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;constexpr&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MapFn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;m&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;noexcept&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{...}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The “Ref” types also can’t be used in a constexpr context (they rely on reinterpret_cast), but the
concepts can be.&lt;/p&gt;

&lt;p&gt;The old stateful, heap-allocated capturing types have been renamed to &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FnBox&lt;/code&gt;, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FnMutBox&lt;/code&gt;, and
&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FnOnceBox&lt;/code&gt; and they still exist for storing a callable past the lifetime of a function. For
instance, &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Iterator::map()&lt;/code&gt; stores its callable as a &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;FnMutBox&lt;/code&gt; on its output iterator. These
types reason for being is to try provide guardrails for binding values instead of pointers, but
these APIs definitely need some love and/or reworking.&lt;/p&gt;

&lt;p&gt;Hopefully I will write a longer post about how the implementation of these concepts works, and the
evolution of all these types.&lt;/p&gt;</content><author><name>Dana Jansens</name></author><category term="Subspace" /><category term="C++" /><summary type="html">I’ve spent a few months mostly working on an enormous branch, so Subspace hasn’t seen a lot of changes on main. Today I merged a lot of stuff, which makes it a good time to mention some of the new things. Slices and Vec Micro post: https://sunny.garden/@blinkygal/110289198753682615 Today in Subspace I finished and merged all the methods for Slice and SliceMut. Slice&amp;lt;T&amp;gt; is a const reference to a contiguous range of const T (like a std::span&amp;lt;const T&amp;gt;), while SliceMut&amp;lt;T&amp;gt; is a mutable reference to a contiguous range of T (like a std::span&amp;lt;T&amp;gt;). Why are these different types? First, this allows the default, shorter wording to be the safer and preferred one: const is default. Secondly, it’s not possible to sort() on a Slice, but if they were one type, it would require all methods that require mutable access to the pointed-to range to be qualified with requires (!std::is_const_v&amp;lt;T&amp;gt;). The API gets harder to understand and to use as a result. This way, the SliceMut type has methods that allow mutation, and Slice simply does not. There are 95 methods in all! I started working on these two months ago on March 3. The Pull Request is here: https://github.com/chromium/subspace/pull/218/. I’m kinda relieved to finally get through them all. There’s lots of useful stuff in there though, including: searching sorting splitting sub-slicing with ranges (including range literals) iterating (forward and reverse) on splits, chunks, and sliding windows joining filling or repeating working with prefixes and suffixes swapping reordering These methods all appear on Vec as well, which is an owning Slice. In addition, Vec is usable in all places that a Slice or SliceMut (if the Vec is not const) would be usable (except as an rvalue, which would allow references to escape from a temporary object). And similarly SliceMut is usuable anywhere a Slice would be. I am sure There’s lots of room for performance tuning, and some TODOs left in this regard. As always everything comes with unit tests, so making future changes can be done with confidence. Range literals In the C++ standard library, the std::span type allows subslicing through the subspan(offset) and subspan(offset, count) overloads (implemented as a default argument). Then, s.subspan(offset) in C++ is equivalent to &amp;amp;s[offset..] in Rust and s.subspan(offset, count) in C++ is equivalent to &amp;amp;s[offset..(offset + count)] in Rust. But Rust is more expressive here. Its RangeBounds can describe any possible range with or without a front edge or back edge. .. is everything, 2.. starts from 2 and goes to the end, ..5 starts at the beginning and stops at 5 (excluding 5), and 2..5 starts at 2 and ends at 5 (excluding 5). It even allows 2..=5 to include 5 in the bounds. Instead of providing a bunch of overloads for this type of expressivity, Subspace slices (and Vec) can be subsliced using the operator[](sus::ops::RangeBounds&amp;lt;usize&amp;gt; auto) operator, which aborts if given a range out of bounds, or get_range(sus::ops::RangeBounds&amp;lt;usize&amp;gt; auto) and get_range_mut(sus::ops::RangeBounds&amp;lt;usize&amp;gt; auto) which return a sus::Option holding nothing if out of bounds or a Slice (or SliceMut respectively) otherwise. The RangeBounds concept is satisfied by a set of types provided in sus::ops that specify a start, an end, both, or neither: sus::ops::Range has a start and end (exclusive). sus::ops::RangeFrom has a start, with an unbounded end. sus::ops::RangeTo has an end, with an unbounded start. sus::ops::RangeFull represented an unbounded start and end. For dynamic values, these can be constructed from numeric values as they are aggegrate types. For constant values, a literal syntax is provided. C++ doesn’t let us have all the nice things, so we can’t use the exact literal syntax from Rust, as C++ requires non-quoted literals to parse as numbers even if you’re going to do the parsing yourself. But we get this: &quot;3..7&quot;_r is a Range&amp;lt;usize&amp;gt; from 3 to 7 (exclusive). &quot;3..=7&quot;_r is a Range&amp;lt;usize&amp;gt; from 3 to 7 (inclusive). &quot;3..&quot;_r is a RangeFrom&amp;lt;usize&amp;gt; that starts at 3. &quot;..7&quot;_r is a RangeTo&amp;lt;usize&amp;gt; that ends at 7 (exclusive). &quot;..&quot;_r is a RangeFull&amp;lt;usize&amp;gt; that represents an unbounded range. Range types can be modified to produce new ranges with the .start_at() and .end_at() methods. These change, or add, a start or end bound, changing the type of the object when a new bound is added. For instance ::sus::ops::RangeFull().start_at(3) makes a ::sus::ops::RangeFrom(3). Mixing literal values with dynamic values is possible as well with these methods, such as a range that starts at 5, but ends at x: &quot;5..&quot;_r.end_at(x). Certainly this is nowhere as nice as Rust’s 5..x syntax. Maybe C++ can give us that one day. But I want to provide the tools to write literal ranges when code authors would benefit from doing so, and the range type aggregate constructors are always available too. Bringing this back to slices, a Vec can produce a slice for any range using its operator[], get_range() or get_range_mut(): auto vec = sus::Vec&amp;lt;i32&amp;gt;::with_values(1, 2, 3, 4, 5); auto s1 = vec[&quot;..&quot;_r]; // A SliceMut over [1, 2, 3, 4, 5]. auto s2 = vec[&quot;3..&quot;_r]; // A SliceMut over [4, 5]. auto s3 = vec[&quot;..3&quot;_r]; // A SliceMut over [1, 2, 3]. auto s4 = vec[&quot;2..3&quot;_r]; // A SliceMut over [3]. Lastly, for signed ranges, there’s also a _rs literal suffix that produces ranges over isize instead of usize, such as &quot;3..12&quot;_rs to make sus::ops::Range&amp;lt;isize&amp;gt;(3, 12). But slices always work with ranges over usize. Iterators While I was slogging through the slice methods, I also added some exciting iterator features. Generator functions Micro post: https://sunny.garden/@blinkygal/110044818048764779 It’s now possible to write an Iterator that will compose nicely with all the existing Iterator methods, but as a single function, instead of having to write a whole type. This is thanks to C++ coroutines, as they allow writing a generator function to modify iteration. This provides a means to write “control flow” into an iteration, in the spirit of https://without.boats/blog/the-registers-of-rust/. Here’s the generator unit tests to demonstrate what I mean. Here the x() function produces an iterator over the set 1, 2, 3, 4. This is composed with the filter() method that keeps only things in the range (1, 4) (exclusive). The result is 2, 3. TEST(IterGenerator, ComposeFromGenerator) { auto x = []() -&amp;gt; Generator&amp;lt;i32&amp;gt; { co_yield 1; co_yield 2; co_yield 3; co_yield 4; }; // Generator is trivially relocatable, so no need to box() it. sus::iter::Iterator&amp;lt;i32&amp;gt; auto it = x().filter([](const i32&amp;amp; a) { return a &amp;gt; 1 &amp;amp;&amp;amp; a &amp;lt; 4; }); EXPECT_EQ(it.next().unwrap(), 2); EXPECT_EQ(it.next().unwrap(), 3); EXPECT_EQ(it.next(), sus::None); } Here the x() function composes with an input iterator, and it does the same as the filter() call above, discarding anything outside the range (1, 4) (exclusive). It is chained with an iterator from a Vec&amp;lt;i32&amp;gt; that contains 1, 2, 3, 4 and again the result is 2, 3. TEST(IterGenerator, ComposeIntoGenerator) { auto x = [](sus::iter::Iterator&amp;lt;i32&amp;gt; auto it) -&amp;gt; Generator&amp;lt;i32&amp;gt; { for (i32 i : it) { if (i &amp;gt; 1 &amp;amp;&amp;amp; i &amp;lt; 4) co_yield i; } }; sus::iter::Iterator&amp;lt;i32&amp;gt; auto it = sus::vec(1, 2, 3, 4).construct&amp;lt;i32&amp;gt;().into_iter().generate(x); EXPECT_EQ(it.next().unwrap(), 2); EXPECT_EQ(it.next().unwrap(), 3); EXPECT_EQ(it.next(), sus::None); } Enumerate Micro post: https://sunny.garden/@blinkygal/110223458907555381 I also have added Iterator::enumerate. Adding the sus::iter::Enumerate type was fairly trivial, but in order to iterate backwards as a sus::iter::DoubleEndedIterator, it needs to know the total length of the iteration sequence. That is, the input iterator needs to also be a sus::iter::ExactSizeIterator. While these concepts were already present in the library, I had not plumbed them through composing iterator types. So that is now done. This means, for example, calling filter() or map() on a DoubleEndedIterator will produce another DoubleEndedIterator. And when possible, such as when calling rev() on an ExactSizeIterator, the resulting reversed iterator will also be an ExactSizeIterator. And in the case of enumerate(), if the input iterator was a DoubleEndedIerator and ExactSizeIterator, the output iterator will also be. Notably, iterators over a Vec, Array or slice are double-ended and have an exact known size and thus satisfy these traits. The same will be true for most Here’s an example where we have an iterator over 1, 2, 3, 4, 5. It is double-ended and its exact size is known. The iterator is reversed with rev(), so it will output 5, 4, 3, 2, 1. Then it is composed with enumerate() which means each step includes the position in the iteration sequence. auto chars = Vec&amp;lt;char&amp;gt;::with_values('a', 'b', 'c', 'd', 'e'); { auto it = sus::move(chars).into_iter().rev().enumerate(); // enumerate() makes an iterator over a Tuple of position and value. using E = sus::Tuple&amp;lt;usize, char&amp;gt;; static_assert(sus::iter::Iterator&amp;lt;decltype(it), E&amp;gt;); // The output iterator of enumerate() is a DoubleSidedIterator and // ExactSizeIterator. static_assert(sus::iter::DoubleEndedIterator&amp;lt;decltype(it), E&amp;gt;); static_assert(sus::iter::ExactSizeIterator&amp;lt;decltype(it), E&amp;gt;); // The output the position paired with the reversed values. for (auto [pos, val]: it) { // Prints: // (0, e) // (1, d) // (2, c) // (3, b) // (4, a) std::cerr &amp;lt;&amp;lt; &quot;(&quot; &amp;lt;&amp;lt; size_t{pos} &amp;lt;&amp;lt; &quot;, &quot; &amp;lt;&amp;lt; val &amp;lt;&amp;lt; &quot;)\n&quot;; } } Side note: In this example, the usize position is casted to a primitive to print, as I did not yet decide on a path for integrating with streams or to otherwise print things. Stdlib Ranges Subspace’s first major compatability hook with the C++ standard library was added, with conversions from sus::iter::Iterator types to C++ ranges. Calling .range() on any iterator will produce a std::ranges::range output, which satisfies the std::ranges::input_range concept. Here are some unit tests that demonstrate the behaviour, showing that the output from .range() is usable as a std::ranges::viewable_range and a std::ranges_input_range. In these examples, the iterator owns the values as vec is consumed to produce the iterator through into_iter(). TEST(CompatRanges, ViewableRange) { sus::Vec&amp;lt;i32&amp;gt; vec = sus::vec(1, 2, 3, 4, 5, 6); // all() requires a `std::ranges::viewable_range`. auto view = std::ranges::views::all(sus::move(vec).into_iter().range()); i32 e = 1; for (i32 i : view) { EXPECT_EQ(e, i); e += 1; } EXPECT_EQ(e, 7); } TEST(CompatRanges, InputRange) { sus::Vec&amp;lt;i32&amp;gt; vec = sus::vec(1, 2, 3, 4, 5, 6); // filter() requires a `std::ranges::input_range`. auto filter = std::ranges::views::filter(sus::move(vec).into_iter().range(), [](const i32&amp;amp; i) { return i &amp;gt; 3; }); i32 e = 4; for (i32 i : filter) { EXPECT_EQ(e, i); e += 1; } EXPECT_EQ(e, 7); } Closures/Callable concepts Fn, FnMut and FnOnce are now concepts, not concrete types, just as the same are traits in Rust. Most notably they manage to take a function signature syntax in their template arguments while still producing useful errors when given a non-matching type. This was rather non-trivial as C++ concepts do not allow partial specialization, and referring to structs in a concept stops good error propogation. Also the template arguments allow some simple pattern matching. sus::fn::Fn&amp;lt;sus::fn::Anything(i32)&amp;gt; auto will match any const-callable thing that takes an i32, regardless of its return type. And sus:fn::Fn&amp;lt;sus::fn::NonVoid(i32)&amp;gt; auto will match any const-callable thing that takes an i32 input and produces some non-void output. I spent a week on this rewrite, which also introduced lightwight FnRef, FnMutRef and FnOnceRef that are concrete types satifying the above concepts and which refer to a function pointer or callable object like a lambda without owning any state. These types are super useful to receive a callable that will be used during a function but not stored beyond the lifetime of the function. And they don’t require your function to become a template, which will help keep down compile times. Most Subspace methods that receive a callable now use these “Ref” implementation types instead of the concepts. Though limitations of type deduction still mean we need to use concept types in some cases, where the callable’s type signature is templated. For example with Option::map() where the return type of the callable determines the output of the map() function, using FnRef&amp;lt;R(T&amp;amp;&amp;amp;)&amp;gt; means the compiler can not deduce the R type: note: 'Option&amp;lt;R&amp;gt; Option&amp;lt;int&amp;gt;::map(sus::fn::FnRef&amp;lt;R(T &amp;amp;&amp;amp;),&amp;gt;) noexcept &amp;amp;&amp;amp;': could not deduce template argument for 'sus::fn::FnRef&amp;lt;R(T &amp;amp;&amp;amp;),&amp;gt;' The result is some pretty unweildly C++ concept bounds, and there’s a ton of space for C++ to improve here in the future: template &amp;lt;::sus::fn::FnOnce&amp;lt;::sus::fn::NonVoid(T&amp;amp;&amp;amp;)&amp;gt; MapFn, int&amp;amp;..., class R = std::invoke_result_t&amp;lt;MapFn&amp;amp;&amp;amp;, T&amp;amp;&amp;amp;&amp;gt;&amp;gt; constexpr Option&amp;lt;R&amp;gt; map(MapFn&amp;amp;&amp;amp; m) &amp;amp;&amp;amp; noexcept {...} The “Ref” types also can’t be used in a constexpr context (they rely on reinterpret_cast), but the concepts can be. The old stateful, heap-allocated capturing types have been renamed to FnBox, FnMutBox, and FnOnceBox and they still exist for storing a callable past the lifetime of a function. For instance, Iterator::map() stores its callable as a FnMutBox on its output iterator. These types reason for being is to try provide guardrails for binding values instead of pointers, but these APIs definitely need some love and/or reworking. Hopefully I will write a longer post about how the implementation of these concepts works, and the evolution of all these types.</summary></entry><entry><title type="html">Me versus C++ Mutable References.</title><link href="http://orodu.net/2023/01/26/mutable-references.html" rel="alternate" type="text/html" title="Me versus C++ Mutable References." /><published>2023-01-26T00:00:00+00:00</published><updated>2023-01-26T00:00:00+00:00</updated><id>http://orodu.net/2023/01/26/mutable-references</id><content type="html" xml:base="http://orodu.net/2023/01/26/mutable-references.html">&lt;p&gt;Me: Let me make a recursive operation. It will recurse through a heterogeneous data structure and
call a function for each &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Thing&lt;/code&gt; it finds.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Q&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;R&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Me: Cool, let’s call it!&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;blast_off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mutable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;C++: No. You can’t pass an rvalue as a mutable reference.&lt;/p&gt;

&lt;p&gt;Me: Right, okay, I guess.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;C++: No. You have a mutable reference to an rvalue now, so you have to move the reference to continue using it as such.&lt;/p&gt;

&lt;p&gt;Me: But I’m not trying to pass ownership, I just want a mutable reference. Move would be misleading to read! Gr.&lt;/p&gt;

&lt;p&gt;C++: Tough. It’s that or write a template.&lt;/p&gt;

&lt;p&gt;Me: Fine. I will make an lvalue for you, for no good reason at all.&lt;/p&gt;

&lt;div class=&quot;language-cpp highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;S&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Q&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;qs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;q&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;R&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;rs&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;r&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;};&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;blast_off&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;auto&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;sus&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FnMut&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_i32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;mutable&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;count&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;});&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;s&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;for_all_things&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Me: But I am also going to blog about it.&lt;/p&gt;</content><author><name>Dana Jansens</name></author><category term="C++" /><summary type="html">Me: Let me make a recursive operation. It will recurse through a heterogeneous data structure and call a function for each Thing it finds. struct Q { Thing t; void for_all_things(sus::fn::FnMut&amp;lt;void(Thing&amp;amp;)&amp;gt;&amp;amp; fn) { fn(t); } } struct R { Thing t; void for_all_things(sus::fn::FnMut&amp;lt;void(Thing&amp;amp;)&amp;gt;&amp;amp; fn) { fn(t); } } struct S { sus::Vec&amp;lt;Q&amp;gt; qs; sus::Vec&amp;lt;R&amp;gt; rs; void for_all_things(sus::fn::FnMut&amp;lt;void(Thing&amp;amp;)&amp;gt;&amp;amp; fn) { for (Q&amp;amp; r: qs) q.for_all_things(fn); for (R&amp;amp; r: rs) r.for_all_things(fn); } }; Me: Cool, let’s call it! void blast_off(S&amp;amp; s) { s.for_all_things([count = 0_i32] (Thing&amp;amp; t) mutable { count += 1; }); } C++: No. You can’t pass an rvalue as a mutable reference. Me: Right, okay, I guess. struct S { sus::Vec&amp;lt;Q&amp;gt; qs; sus::Vec&amp;lt;R&amp;gt; rs; void for_all_things(sus::fn::FnMut&amp;lt;void(Thing&amp;amp;)&amp;gt;&amp;amp;&amp;amp; fn) { for (Q&amp;amp; r: qs) q.for_all_things(fn); for (R&amp;amp; r: rs) r.for_all_things(fn); } }; C++: No. You have a mutable reference to an rvalue now, so you have to move the reference to continue using it as such. Me: But I’m not trying to pass ownership, I just want a mutable reference. Move would be misleading to read! Gr. C++: Tough. It’s that or write a template. Me: Fine. I will make an lvalue for you, for no good reason at all. struct S { sus::Vec&amp;lt;Q&amp;gt; qs; sus::Vec&amp;lt;R&amp;gt; rs; void for_all_things(sus::fn::FnMut&amp;lt;void(Thing&amp;amp;)&amp;gt;&amp;amp; fn) { for (Q&amp;amp; r: qs) q.for_all_things(fn); for (R&amp;amp; r: rs) r.for_all_things(fn); } }; void blast_off(S&amp;amp; s) { auto fn = sus::fn::FnMut&amp;lt;void(Thing&amp;amp;)&amp;gt;([count = 0_i32] (Thing&amp;amp; t) mutable { count += 1; }); s.for_all_things(fn); } Me: But I am also going to blog about it.</summary></entry></feed>