ProgrammingPro #43: Rust Async Insights, .NET 9 Cloud-AI Boost, Go's TIOBE Rise, GitHub AI Accelerator, and Efficient Parallel Java
Bite-sized actionable content, practical tutorials, and resources for programmers
Welcome to this week’s edition of ProgrammingPro!
In today’s Expert Insight, we bring you an excerpt from the book, Asynchronous Programming in Rust, that talks about the necessity of runtimes for managing asynchronous tasks, focusing on scheduling and driving Rust's Future trait implementations.
News Highlights: Microsoft's .NET 9 focuses on enhancing cloud-native and AI app development; DoorDash uses CockroachDB for microservice config management; Go hits Tiobe index top 10; and GitHub's AI-focused Accelerator invites applications.
My top 5 picks from today’s learning resources:
But there’s more, so dive right in.
Stay Awesome!
Divya Anne Selvaraj
Editor-in-Chief
🗞️News and Analysis🔎
Microsoft reveals what’s to come in .NET 9: Microsoft's .NET 9 aims to enhance cloud-native and AI app development with a focus on runtime performance, application monitoring, and integration with services like Kubernetes and Redis. Read to learn more about the strategic directions Microsoft is taking with .NET 9.
DoorDash Uses CockroachDB to Create Config Management Platform for Microservices: The platform simplifies architecture, enhances lifecycle management of configuration values, and supports experimentation. Read to learn about the innovative approach DoorDash took to overcome the complexities of configuration management in microservices.
Preserving the magic of free: new types of licenses will not solve open source business model, says Percona founder: Peter Zaitsev critiques new licensing models for open source, emphasizing they won't address the core business challenges and will risk essential freedom that fuels innovation. Read to gain insights into the complexities of sustaining an open source business model.
GitHub Accelerator applications now open for AI-focused open-source projects: GitHub has launched a new Accelerator cohort focusing on AI in open-source, offering $40,000 in funding, support, and resources to selected projects. Read to understand the program’s requirements and benefits.
Go language hits top 10 in the Tiobe index: Initially launched in 2009, Go faced fluctuations in popularity but has seen a resurgence thanks to its use in projects like Docker and Kubernetes, and a consistent half-year release cycle. Read to learn more and discover the top languages in this month’s rankings.
🎓Tutorials and Learning Resources💡
Python
🎓Tutorial | BNF Notation: Dive Deeper Into Python's Grammar: Backus-Naur Form (BNF) notation, is a metasyntax to express the grammar of Python. Read to learn how to use the notation to improve your Python code.
🎓Tutorial | Going Further with CUDA for Python Programmers: This video delves into optimizing CUDA performance, focusing on memory optimization. Watch for a comparison of approaches, including pure Python, Numba, and raw CUDA.
Primer on Python Decorators: This resource covers practical applications and best practices. Read for illustrative examples demonstrating how to use decorators for timing functions, debugging, slowing down code, and more.
For more Python resources go to PythonPro
C/C++/C#
C can be memory-safe: This article challenges the notion that rewriting system code is the only solution to achieve memory safety. Read to learn how Modern C compilers can enforce memory safety through minor, compatible changes.
C++ Package Managers: This resource covers a wide range of options from vcpkg and Conan to newer tools like Xrepo. Read for insights into their suitability for different project sizes, library types, and developer experience levels.
How to Handle Null References in the Latest Version of C#: C# 12 introduced enhanced null reference handling, focusing on preventing null-related errors. Read to learn advanced techniques for handling null references in C#.
Java
Optimizing Java for Modern Hardware: the Continuous Evolution of the Vector API: The latest update extends vector access to heap MemorySegments with any primitive element type. Read for insights into the language's capacity for high-performance computations.
🎓Tutorial | Leveraging Java's Fork/Join Framework for Efficient Parallel Programming: This resource details the framework's capabilities in dividing and executing tasks across multiple cores. Read for insights into optimizing task execution through strategic task design and pool management.
🎓Tutorial | Advanced Brain-Computer Interfaces (BCIs) With Java: This in-depth guide takes you through creating applications, using NeuroSky's MindWave Mobile headset and Java SDK. Read to learn how to connect to and utilize BCI devices and process and interpret brainwave data.
Go
🎓Tutorial | How I write HTTP services in Go after 13 years: This guide offers insights into structuring servers and handlers, middleware use, testing strategies, and more. Read to learn how to build robust HTTP services.
Go composable iterator functions: This article discusses an upcoming feature in Go. Read to discover how it can be leveraged to create composable and lazily evaluated operations for efficient data handling.
JavaScript and TypeScript
Deep JavaScript: Theory and Techniques: This book is a comprehensive guide that covers advanced JavaScript topics. Read for insights into topics like type coercion, destructuring, environment management, and more.
🎓Tutorial | How to Use the App Directory in Next.js: This guide focuses on advanced features like Layouts for interface consistency, folder-based Routing for navigation, and specialized components for error handling. Read to learn how to effectively structure and manage a Next.js project.
🎓Tutorial | Build and publish an npx command to npm with Typescript: The guide covers configuring package.json, bundling Typescript code into a single JavaScript file, and publishing to npm. Read to access a a step-by-step guide for the entire process.
PHP
🎓Tutorial | Creating an Automated Documentation Pipeline in PHP with Autodocs and GitHub Actions: This guide covers creating a demo app with Minicli, configuring Autodocs, and defining documentation pages. Read to learn how to automate the documentation build process.
SQL
🎓Tutorial | SQL Query Performance Tuning in MySQL: Using composite indexes in MySQL can significantly reduce query response times. Read to learn how to design, implement, and leverage these indexes.
.NET
🎓Tutorial | ABP Suite - Best CRUD Page Generation Tool for .NET: ABP Suite is a superior CRUD page generation tool for .NET developers, offering extensive UI support and mobile options. Read to learn how to leverage the tool for generating CRUD pages, handling relationships, and customizing code.
Rust
What’s new in Rust 1.76: Rust 1.76 introduces ABI compatibility updates, with documentation now detailing ABI-compatible function signatures, including guaranteed compatibility between char and u32. Read to learn more.
Ruby
🎓Tutorial | How to find an element inside a Ruby array: This article details the use of .include?, .last, .first, .find, .select with indexing for nth matches, and .index/.find_index for element positions. Read to learn practical Ruby array manipulation techniques.
Swift
How to use @ScaledMetric in SwiftUI for Dynamic Type support: While SwiftUI's text elements adjust to dynamic type sizes automatically, other UI components do not. Read to learn the importance of dynamic type support for accessibility and how to implement @ScaledMetric.
Microservices
Exploring Microservices Architecture with NestJS: This article delves into patterns like CQRS, Saga, Event Bus, and Circuit Breaker to build scalable and maintainable systems. Read to gain an understanding of key microservices patterns.
🌟Best Practices and Code Optimization🚀
The Best Way To Diagnose a Patient Is To Cut Him Open: This comprehensive resource critiques the reliance on print statements for debugging, advocating for modern techniques like tracepoints and object marking. Read to learn how to utilize
advanced debugger features.
🎓Tutorial | Code Search Using Retrieval Augmented Generation (RAG): This guide outlines creating vector indices for code files using embeddings and querying these indices to assist developers in navigating codebases. Read to learn how to identify relevant code sections faster and more accurately.
The High-Risk Refactoring: This article outlines the complexities and risks associated with code refactoring, especially in AI environments, and suggests measures to mitigate potential negative impacts on business and development. Read to learn how to navigate high-risk refactoring efforts.
Give us feedback or request a resource for next week
🧠 Expert Insight 📚
Here’s an exclusive excerpt from “Chapter 8, Runtimes, Wakers, and the Reactor-Executor Pattern” in the book, Asynchronous Programming in Rust by Carl Fredrik Samson.
Introduction to runtimes and why we need them
You need to bring your own runtime for driving and scheduling asynchronous tasks in Rust.
Runtimes come in many flavors, from the popular Embassy embedded runtime,
which centers more on general multitasking
and can replace the need for a real-time operating system (RTOS) on many platforms, to Tokio, which centers on non-blocking I/O on popular server and desktop operating systems.
All runtimes in Rust need to do at least two things: schedule and drive objects implementing Rust’s Future trait to completion. Going forward in this chapter, we’ll mostly focus on runtimes for doing non-blocking I/O on popular desktop and server operating systems such as Windows, Linux, and macOS. This is also by far the most common type of runtime most programmers will encounter in Rust.
Taking control over how tasks are scheduled is very invasive, and it’s pretty much a one-way street. If you rely on a userland scheduler to run your tasks, you cannot, at the same time, use the OS scheduler (without jumping through several hoops), since mixing them in your code will wreak havoc and might end up defeating the whole purpose of writing an asynchronous program.
The following diagram illustrates the different schedulers:
Figure 8.1 – Task scheduling in a single-threaded asynchronous system
An example of yielding to the OS scheduler is making a blocking call using the default std::net ::TcpStream or std::thread::sleep methods. Even potentially blocking calls using primitives such as Mutex provided by the standard library might yield to the OS scheduler.
That’s why you’ll often find that asynchronous programming tends to color everything it touches, and it’s tough to only run a part of your program using async/await.
The consequence is that runtimes must use a non-blocking version of the standard library. In theory, you could make one non-blocking version of the standard library that all runtimes use, and that was one of the goals of the async_std initiative. However, having the community agree upon one way to solve this task was a tall order and one that hasn’t really come to fruition yet.
Before we start implementing our examples, we’ll discuss the overall design of a typical async runtime in Rust. Most runtimes such as Tokio, Smol, or async-std will divide their runtime into two parts.
The part that tracks events we’re waiting on and makes sure to wait on notifications from the OS in an efficient manner is often called the reactor or driver.
The part that schedules tasks and polls them to completion is called the executor.
Let’s take a high-level look at this design so that we know what we’ll be implementing in our example.
Reactors and executors
Dividing the runtime into two distinct parts makes a lot of sense when we take a look at how Rust models asynchronous tasks. If you read the documentation for Future and Waker, you’ll see that Rust doesn’t only define a Future trait and a Waker type but also comes with important information on how they’re supposed to be used.
One example of this is that Future traits are inert. Another example is that a call to Waker::wake will guarantee at least one call to Future::poll on the corresponding task.
So, already by reading the documentation, you will see that there is at least some thought put into how runtimes should behave.
The reason for learning this pattern is that it’s almost a glove-to-hand fit for Rust’s asynchronous model.
Since many readers, including me, will not have English as a first language, I’ll explain the names here at the start since, well, they seem to be easy to misunderstand.
If the name reactor gives you associations with nuclear reactors, and you start thinking of reactors as something that powers, or drives, a runtime, drop that thought right now. A reactor is simply something that reacts to a whole set of incoming events and dispatches them one by one to a handler. It’s an event loop, and in our case, it dispatches events to an executor. Events that are handled by a reactor could be anything from a timer that expires, an interrupt if you write programs for embedded systems, or an I/O event such as a READABLE event on TcpStream.
You could have several kinds of reactors running in the same runtime.
If the name executor gives you associations to executioners (the medieval times kind) or executables, drop that thought as well. If you look up what an executor is, it’s a person, often a lawyer, who administers a person’s will. Most often, since that person is dead. Which is also the point where whatever mental model the naming suggests to you falls apart since nothing, and no one, needs to come in harm’s way for the executor to have work to do in an asynchronous runtime, but I digress.
The important point is that an executor simply decides who gets time on the CPU to progress and when they get it. The executor must also call Future::poll and advance the state machines to their next state. It’s a type of scheduler.
It can be frustrating to get the wrong idea from the start since the subject matter is already complex enough without thinking about how on earth nuclear reactors and executioners fit in the whole picture.
Since reactors will respond to events, they need some integration with the source of the event. If we continue using TcpStream as an example, something will call read or write on it, and at that point, the reactor needs to know that it should track certain events on that source.
For this reason, non-blocking I/O primitives and reactors need tight integration, and depending on how you look at it, the I/O primitives will either have to bring their own reactor or you’ll have a reactor that provides I/O primitives such as sockets, ports, and streams.
Packt subscribers can continue reading for free here. Not a Packt library subscriber yet? Here’s a newletter exclusive 50% off your first month. Asynchronous Programming in Rust by Carl Fredrik Samson was published in February 2023. You can buy the book here.
🛠️ Useful Tools ⚒️
memray: an open-source memory profiler for Python, designed to track and analyze memory allocations.
pkl: an open-source configuration as code language, offering extensive validation and tooling
hono: a fast, lightweight, and versatile web framework for building ultrafast web applications on various JavaScript runtimes, including Cloudflare Workers, Deno, and Node.js.
gitbutler: a version control client powered by Git, Tauri, Rust, and Svelte, designed for efficient and secure code management.
That’s all for today.
We have an entire range of newsletters with focused content for tech pros. Subscribe to the ones you find the most useful here. Complete ProgrammingPro archives can be found here. Complete PythonPro archives are here.
📢 If your company is interested in reaching an audience of developers, software engineers, and tech decision makers, you may want to advertise with us.
If you have any feedback, leave a comment below.