<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Michael Hentges on Medium]]></title>
        <description><![CDATA[Stories by Michael Hentges on Medium]]></description>
        <link>https://medium.com/@mikehentges65?source=rss-8b3e41d73ef4------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*E4UjSjTnu6Q9pF_lFRtFcQ.jpeg</url>
            <title>Stories by Michael Hentges on Medium</title>
            <link>https://medium.com/@mikehentges65?source=rss-8b3e41d73ef4------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Wed, 20 May 2026 13:30:29 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@mikehentges65/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Rust’s Great Error Handling]]></title>
            <link>https://medium.com/better-programming/rusts-great-error-handling-e6c4aa5656da?source=rss-8b3e41d73ef4------2</link>
            <guid isPermaLink="false">https://medium.com/p/e6c4aa5656da</guid>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[error-handling]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Michael Hentges]]></dc:creator>
            <pubDate>Mon, 17 Apr 2023 15:58:56 GMT</pubDate>
            <atom:updated>2023-04-17T15:58:56.215Z</atom:updated>
            <content:encoded><![CDATA[<h4>How Rust’s error handling is better than other programming languages</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t0HKdPmQUyTyJ7ExfJmEbg.jpeg" /><figcaption>© fizkes/Adobe Stock — licensed for use</figcaption></figure><p>Handling errors is hard, but Rust’s error-handling capabilities give developers great tools for the job. Handling errors well is one of the key characteristics of great code. Every developer desires solid, robust code that does its job and doesn’t crash — and is easy to maintain. The language designers of Rust have provided unique means of defining and handling errors fundamentally different from the programming languages that preceded it — and is a key contributor to why developers have continuously voted Rust the “most loved language” on Stack Overflow’s yearly developer survey.</p><p>This article will examine the differences between Rust language’s error-handling mechanisms and prior programming languages and how Rust’s approach is better.</p><h3>In the Beginning</h3><p>The C programming language was introduced in the 1970s and became the first widely popular coding language. But its error-handling mechanisms are rudimentary. The following is very typical and idiomatic C code for reading from a file:</p><pre>void a_test() {<br>  FILE *fp = fopen(&quot;filename&quot;, &quot;r&quot;);<br>  char buff[255];<br><br>  while(fread(buff, 1, 255, fp)) {<br>  // do something with buff<br>  }<br>}</pre><p>What’s surprising about this code (at least to non-C developers) is that fread() does not return a bool value — it returns a value of type size_t (or int), which is the number of bytes read. The rules of C state that the integer value 0 is treated as false; anything else is true. So our loop keeps going as long as the fread() function returns some bytes. When a zero is returned, an error happens, or we’ve reached the end of the file.</p><p>This pattern of intermixing valid return values with error codes is pervasive in C and most other programming languages, where a function can only return a single value. C has a global ERRNO value that may have more information about what went wrong, but in general, you don’t get a lot of extra data about what kind of error occurred. When you can only return one value from a function and want the function to return useful data, you must mix error codes with the return values.</p><p>This can cause problems — the return value must not be in the set of valid values. We see -1 and 0 used as “magical error return values” in many functions to indicate an error has occurred.</p><h3><strong>Structured Error Handling</strong></h3><p>To do better, C++ (and later, Java) introduced structured error handling — the try/catch/throw mechanism that allowed errors to be triggered in one place, have additional data attached to the error, and differentiate between different kinds of errors.</p><pre>#include &lt;iostream&gt;<br><br>int call_a_function() {<br>    std::cout &lt;&lt; &quot;inside of my function&quot; &lt;&lt; std::endl;<br>    throw(&quot;some error&quot;);<br>    std::cout &lt;&lt; &quot;we will not get this far&quot; &lt;&lt; std::endl;<br>    return 42;<br>}<br><br>int main(int argc, const char * argv[]) {<br>    int my_answer;<br>    std::cout &lt;&lt; &quot;Hello, World!&quot; &lt;&lt; std::endl;<br>    try {<br>        my_answer = call_a_function();<br>    } catch (const char* s) {<br>        std::cout &lt;&lt; &quot;An error has occurred: &quot; &lt;&lt; s &lt;&lt; std::endl;<br>        my_answer = -1;<br>    }<br>    std::cout &lt;&lt; &quot;My answer is: &quot; &lt;&lt; my_answer &lt;&lt; std::endl;<br>    return 0;<br>}<br><br>Output:<br>Hello, World!<br>inside of my function<br>An error has occurred: some error<br>My answer is: -1</pre><p>While exception handling did allow for cleanly separating errors from valid return values, this approach has fundamental problems. People have written lengthy arguments about the issues with structured error handling (<a href="https://www.lighterra.com/papers/exceptionsharmful/">Exception Handling Considered Harmful</a>, for example). The main drawback with exception handling is that it introduces an alternate flow path for a program that is hard to see as a developer, much less control.</p><p>When the exception is thrown, we do not know who will catch it — it could be several functions up the call stack. Also, a function catching an exception might be surprised by who threw the exception — we cannot choose which functions we want to catch exceptions from — anything that runs underneath our called function is possible.</p><p>Exception handling also introduces problems in multi-threaded applications — separating exception handling across threads is difficult. Java’s requirement that any function that could throw an exception list the exceptions in its method definition is also repetitive and verbose.</p><p>Structured error handling is not meant for “normal” error conditions — it is meant for errors that disrupt an application’s normal flow, not routine return values from a function. A string find operation that returns the position of a character inside of a string wouldn’t throw an exception if it can’t find the intended character — it would instead return some “not found” result.</p><p>The C function strchr() does this — it returns either a pointer to the character found or the magical value null, indicating failure. You wouldn’t expect this function to throw an exception if it can’t find a character. Exception handling only attempts to solve part of the problem of returning error conditions out of functions.</p><h3><strong>A Different Approach</strong></h3><p>Go took a novel approach — it allowed functions to return multiple values instead of one. You will frequently see the following in Golang code:</p><pre>return_value, err = my_func();<br>if err != nil {<br> return err;<br>}</pre><p>Here, both the error code and the value you need are returned from a function — they do not try to occupy the same spot. But the error value placeholder is present when an error does not occur. It’s up to the programmer to check the error value and see if err != nil statements are littered throughout Go code. But sometimes you don’t want to deal with the error — there’s nothing useful to do — and you want to return it from your function. There is a significant amount of boilerplate error-handling code in Go programs to check for errors on each function call in an application, as there is no other way to handle the error.</p><h3><strong>Rust’s Approach</strong></h3><p>Rust has a novel approach to error handling that leverages Rust’s enumerations. Enumerations in Rust are algebraic data types — they support data values, not just constants. Two special-purpose enumerations are commonly used to handle return values: Option and Result. Option is used when a function may or may not return a useful value — you get a Some(value) or None as your values.</p><p>Using our find a character in a string example, the Rust function find returns an Option enumeration — it will have a value of Some(x) with x being the character’s position or a value of None. You can use Rust’s pattern-matching mechanism to handle these conditions elegantly. Here’s how to do that:</p><pre>let a_string = &quot;testing&quot;.to_string();<br>let my_result = a_string.find(&#39;a&#39;);<br><br>match my_result {<br>    Some(pos) =&gt; println!(&quot;I found the character at position: {}&quot;, pos),<br>    None =&gt; println!(&quot;I did not find the character&quot;)<br>}</pre><p>Here we have a precise mechanism for knowing if the function returned a valid value or did not work. You do not have to check for a magical “error value”; the enumeration separates the result from the error condition.<br> <br>Rust’s pattern-matching rules require that your code handle both cases of the enumeration — you can’t silently skip error handling as the compiler enforces it. But Rust has a ? operator that allows you to stop and return None if that’s what comes back from a function. The following two code snippets are identical in function:</p><pre>fn string_test() -&gt; Option&lt;usize&gt; {<br>    let a_string = &quot;testing&quot;.to_string();<br>    if let Some(pos) = a_string.find(&#39;a&#39;) {<br>        println!(&quot;I found the character at position: {}&quot;, pos);<br>        Some(pos)<br>    } else {<br>        None<br>    }<br>}</pre><p>and</p><pre>fn string_test() -&gt; Option&lt;usize&gt; {<br>    let a_string = &quot;testing&quot;.to_string();<br>    let pos = a_string.find(&#39;a&#39;)?;<br>    println!(&quot;I found the character at position: {}&quot;, pos);<br><br>    Some(pos)<br>}</pre><p>The ? operator “unwraps” the Option, pulling the value out or returning from the calling function the value None if it is not there. Note that we are not bypassing the error handling — it is still occurring, and the Rust compiler does it for us automatically. This is very clean and does not clutter the code — making it easier to follow the flow of the function.</p><p>The second enumeration used in error handling is Result. The two possible values of Result are Ok(T) and Err(E). Here we are returning an error value and not a plain None to pass more information back to the calling function when there is an error. Here is an example function that is reading a file, where it returns an error to the calling function if the file does not exist or there is some other type of error reading the file:</p><pre>fn get_data(filename: &amp;str) -&gt; Result&lt;String, Error&gt; {<br>    let mut my_file = File::open(filename)?;<br>    let mut buffer = String::new();<br><br>    let bytes_read = my_file.read_to_string(&amp;mut buffer)?;<br>    Ok(format!(&quot;read {} bytes, string is:\n{}&quot;, bytes_read, buffer))<br>}</pre><p>Here, our function returns a Result, containing either a String value or an error of type Error. We again use the ? operator on the read_to_string() function, which unwraps the number of bytes read on a successful return; otherwise, it returns an Err to the calling function. We print out the number of bytes read and the content of our data file on a successful read. A simple main() that calls this function could look like this:</p><pre>fn main() {<br>    match get_data(&quot;data.txt&quot;) {<br>        Ok(my_string) =&gt; println!(&quot;result: &lt;{}&gt;&quot;, my_string),<br>        Err(e) =&gt; eprintln!(&quot;error of: {}&quot;, e),<br>    };<br>}</pre><p>When we run our program and have a valid data.txt file, we can see the following output:</p><pre>% cargo run<br>    Finished dev [unoptimized + debuginfo] target(s) in 0.00s<br>     Running `target/debug/rust_examples`<br>result: &lt;read 65 bytes, string is:<br>This is some data in a text file.<br>Here is a second line of data.<br>&gt;</pre><p>When the data file does not exist, here is what our output looks like:</p><pre>% cargo run<br>    Finished dev [unoptimized + debuginfo] target(s) in 0.00s<br>     Running `target/debug/rust_examples`<br>error of: No such file or directory (os error 2)</pre><p>We see that a complete error was propagated back to our main function, where we captured it in our match statement and could print it out to the console.</p><p>Our simple examples demonstrate how Rust’s error handling enables cleaner code, where an application can elegantly handle the various conditions that calling an external function might produce. The ? operator with the Option and Result enumerations allow for a very clean means of testing for errors, handling them appropriately, and not relying on mixing valid values and error codes. An ability to attach meaningful data to error conditions makes for more robust APIs.</p><h3><strong>But There’s More!</strong></h3><p>Rust’s standard libraries have various specific functions for managing error states and mapping library errors into user-defined values. External libraries like the thiserror crate extend Rust’s standard error handling. The thiserror crate provides an easy-to-use means of defining specific errors that look and feel exactly like the built-in error types that Rust defines: (example derived from thiserror on docs.rs).</p><pre>use thiserror::Error;<br><br>#[derive(Error, Debug)]<br>pub enum DataStoreError {<br>    #[error(&quot;data store disconnected&quot;)]<br>    Disconnect(#[from] io::Error),<br>    #[error(&quot;the data for key `{0}` is not available&quot;)]<br>    Redaction(String),<br>    #[error(&quot;invalid header (expected {expected:?}, found {found:?})&quot;)]<br>    InvalidHeader {<br>        expected: String,<br>        found: String,<br>    },<br>    #[error(&quot;unknown data store error&quot;)]<br>    Unknown,<br>}</pre><p>The anyhow crate defines a flexible means of handling multiple error types and adding application-specific data to errors. Anyhow allows for Result&lt;T, anyhow::Error&gt;, or equivalently, anyhow::Result&lt;T&gt;, as the function&#39;s return type. A function can then return any error type that implements the std::error::Error trait. (Example derived from anyhow on docs.rs).</p><pre>use anyhow::Result;<br><br>fn get_cluster_info() -&gt; Result&lt;ClusterMap&gt; {<br>    // plain return of an error, here likely related to file system errors<br>    let config = std::fs::read_to_string(&quot;cluster.json&quot;)?;<br>    <br>    // return of an error with context<br>    let map: ClusterMap = serde_json::from_str(&amp;config)<br>        .context(“json deserialization failed, file cluster.json likely not formatted correctly”)?;<br>    <br>    Ok(map)<br>}</pre><h3><strong>In Summary</strong></h3><p>Our simple examples show how Rust’s error handling significantly differs from prior common programming languages. Rust provides a clean means of separating good vs. bad results from functions without introducing boilerplate that litters our source code. And we have the tools needed to define new error types that work cleanly within our library or application.</p><p>Designing appropriate error handling is still hard — but Rust at least gives us the tools to do a good job of it! And this is more than aesthetics, better error handling allows errors to be more visible in an application and decreases application defects due to not handling error conditions correctly.</p><p>It also contributes to cleaner and simpler designs — by definition, better designs — which make applications more robust and easier to develop correctly. This is a solid contributor to the Rust programming language’s high interest and “love” from developers!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e6c4aa5656da" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/rusts-great-error-handling-e6c4aa5656da">Rust’s Great Error Handling</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Rust, Lambda, and DynamoDB]]></title>
            <link>https://medium.com/better-programming/rust-lambda-and-dynamodb-bea841d47cca?source=rss-8b3e41d73ef4------2</link>
            <guid isPermaLink="false">https://medium.com/p/bea841d47cca</guid>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[dynamodb]]></category>
            <category><![CDATA[terraform]]></category>
            <category><![CDATA[aws]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Michael Hentges]]></dc:creator>
            <pubDate>Fri, 18 Nov 2022 21:39:36 GMT</pubDate>
            <atom:updated>2022-11-18T21:39:36.442Z</atom:updated>
            <content:encoded><![CDATA[<h4>Implementation of an AWS Lambda function in Rust, that writes to DynamoDB. All deployed through Terraform, with a Rust HTTP Client as well!</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/720/1*6ZUEm_XDzWR0JbFrQ32ScA.png" /><figcaption>Rust in Action</figcaption></figure><p>As part of my journey to learn more about Rust development, I developed a Lambda service hosted on AWS that writes to a DynamoDB database and an associated Rust HTTP client. Along with Rust, I used Terraform to manage the deployment of the AWS resources. This article is the 4th I’ve written on my Wireless Thermostat application that runs on a Raspberry Pi. You can find the others here: <a href="https://medium.com/@mikehentges65/raspberry-pi-wireless-thermostat-in-rust-45a5d35196cf">Raspberry Pi Wireless Thermostat — in Rust</a>, <a href="https://medium.com/@mikehentges65/rust-cross-compiling-made-easy-e30fcb233fef">Rust Cross Compiling Made Easy</a>, and <a href="https://betterprogramming.pub/easy-multi-threaded-shared-memory-in-rust-57344e9e8b97">Implementing Multi-Threaded Shared Memory in Rust</a>. All source code is available at <a href="https://github.com/mikehentges/thermostat-pi">my GitHub repository</a>.</p><p>We are going to cover the following in this article:</p><ol><li>Define a JSON API as a separate crate shared across two related projects.</li><li>Write an AWS Lambda function in Rust, using the AWS Rust SDK, that accepts an HTTP POST with a JSON payload of data, and writes the data to a DynamoDB database.</li><li>Use Terraform to define and build the database, the lambda function, and the permissions glue required to have all the pieces fit together.</li><li>Use the AWS CLI to deploy Lambda application executable updates.</li><li>Write a Rust HTTP Client that sends the data to our Lambda function.</li></ol><p>I assume you have the <a href="https://aws.amazon.com/cli/">AWS CLI</a>, <a href="https://www.terraform.io/">Terraform</a>, and <a href="https://www.rust-lang.org/">Rust</a> installed on your system, and your AWS account is set up and connected to the CLI. It’s a bit of work, but easy to follow under each system’s documentation.</p><p>The use case for my application is to keep track of my Raspberry Pi thermostat application’s status and record history. A Rust application running on a Raspberry Pi will push information to a cloud database. With activity data in a cloud database, monitoring the application’s health can be done by examining the data — and avoiding having to open firewalls to let in outside observers. I also get a data source for history, which I can graph on a UI later.</p><p>I picked <a href="https://aws.amazon.com/dynamodb/">DynamoDB on AWS</a> as the database platform. My data needs fit easily inside DynamoDB’s free tier, and DynamoDB is an effective place to push IoT time series data. Instead of directly connecting the Pi application to the Dynamo database, I chose an HTTP-based service layer for the interface between the Raspberry PI and AWS. I’ve found HTTP services to be more resilient than direct DB connections — HTTP’s stateless nature makes it self-correcting across network outages. Pushing data through to a DB is an excellent job for a Lambda function — and with AWS recently publishing a Rust SDK, I took the opportunity to build out the Lambda function as a Rust application. Here’s a picture of how the pieces fit together that we are going to examine:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/461/1*tiTMKJF4fkGnKHFd_vtSRw.png" /><figcaption>Our Architecture</figcaption></figure><p>There are three main parts to the application. First, the main application is thermostat_pi, the client that creates the data we move to the database. Under this project is the Lambda function project, named push_temp. Lastly, the temp_data project holds a definition of a data transport API. All three projects are on GitHub under the thermostat_pi application.</p><p>In temp_data, I started with a Rust struct that holds the data pieces for the thermostat application and enabled serde for JSON representation:</p><pre>//temp-data/src/lib.rs<br>use serde::Deserialize;<br>use serde::Serialize;<br><br>#[derive(Debug, Serialize, Deserialize)]<br>pub struct TempData {<br> pub record_date: String,<br> pub thermostat_on: bool,<br> pub temperature: f32,<br> pub thermostat_value: u16,<br>}</pre><p>I created this in a separate Rust crate so that it could be shared with both the Pi application and lambda function projects — ensuring both always were in sync. The temp-data Cargo.toml looks like this:</p><pre>[package]<br>name = &quot;temp-data&quot;<br>version = &quot;0.1.0&quot;<br>edition = &quot;2021&quot;<br>license = &quot;MIT&quot;<br><br>[dependencies]<br>serde = {version = &quot;1&quot;, features = [&quot;derive&quot;]}</pre><p>I then defined a corresponding DynamoDB database to hold this information. I decided on a Partition Key of “day” for the time-series data, which allows for retrieving a day’s worth of data without scanning the entire table. I also created a sort key for the date/time. This key structure will allow efficient read access to the data when I want to set up an alarm or graph historical data. I don’t have much experience with DynamoDB, so there could be a more efficient way to solve this problem — but what I have works for me. Here’s what the DynamoDB table will look like when we are finished:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/624/1*qDwq6zApoFqFqbspTekYEg.png" /><figcaption>Sample Data in Dynamo</figcaption></figure><p>The Record_Day and Record_Date keys are strings to DynamoDB. The Record_Date format is RFC3339, which the Rust standard time package supports. It creates a string that can sort the time values correctly by alphabetical sorting.</p><p>Next, we build the lambda function to take our incoming request and store it in the DynamoDB table. The push-temp directory of my main project (GitHub link) is where this lives. The push-temp Cargo.toml contains these entries:</p><pre>[package]<br>name = &quot;push_temp&quot;<br>version = &quot;0.1.0&quot;<br>edition = &quot;2021&quot;<br>license = &quot;MIT OR Apache-2.0&quot;<br><br>[dependencies]<br>aws-config = &quot;0.51.0&quot;<br>aws-sdk-dynamodb = &quot;0.21.0&quot;<br>log = &quot;0.4.14&quot;<br>serde = {version = &quot;1&quot;, features = [&quot;derive&quot;]}<br>tokio = &quot;1.16.1&quot;<br>tracing-subscriber = { version = &quot;0.3&quot;, features = [&quot;env-filter&quot;] }<br>lambda_http = &quot;0.7&quot;<br>serde_json = &quot;1.0.78&quot;<br><br># Our package that defines the struct of the incoming request<br>temp-data = { path=&quot;../temp-data&quot; }</pre><p>We are using the <a href="https://docs.aws.amazon.com/sdk-for-rust/">AWS SDK for Rust</a>. I put all the Rust code in the main.rs file for our lambda function. First, there is some boilerplate to import our message struct, define our response types, and get the Lambda environment set up:</p><pre>//push-temp/src/main.rs<br>use aws_sdk_dynamodb::model::AttributeValue;<br>use aws_sdk_dynamodb::Client;<br>use lambda_http::{lambda_runtime::Error, service_fn, IntoResponse, Request};<br><br>extern crate temp_data;<br>use temp_data::TempData;<br><br>use log::{debug, error};<br>use serde::Serialize;<br><br>#[derive(Debug, Serialize)]<br>struct SuccessResponse {<br> pub body: String,<br>}<br><br>#[derive(Debug, Serialize)]<br>struct FailureResponse {<br> pub body: String,<br>}<br><br>// Implement Display for the Failure response so that we can then implement Error.<br>impl std::fmt::Display for FailureResponse {<br> fn fmt(&amp;self, f: &amp;mut std::fmt::Formatter&lt;&#39;_&gt;) -&gt; std::fmt::Result {<br> write!(f, &quot;{}&quot;, self.body)<br> }<br>}<br><br>impl std::error::Error for FailureResponse {}</pre><p>The main() function registers an event handler for the incoming event; our handler function is named my_handler:</p><pre>//push-temp/src/main.rs (continued)<br>#[tokio::main]<br>async fn main() -&gt; Result&lt;(), Error&gt; {<br> tracing_subscriber::fmt::init();<br> debug!(&quot;logger has been set up&quot;);<br> lambda_http::run(service_fn(my_handler)).await?;<br> Ok(())<br>}</pre><p>Our my_handler() function will run when an incoming request arrives. Our my_handler() function needs to do a couple of things. First, it grabs the incoming JSON from the request and parses it into our struct, request_struct. Notice that if the JSON parsing fails, an error value returns at this point.</p><pre>//push-temp/src/main.rs (continued)<br>async fn my_handler(request: Request) -&gt; Result&lt;impl IntoResponse, Error&gt; {<br>  debug!(&quot;handling a request, Request is: {:?}&quot;, request);<br>  let request_json = match request.body() {<br>    lambda_http::Body::Text(json_string) =&gt; json_string,<br>    _ =&gt; &quot;&quot;,<br>  };<br>  debug!(&quot;Request JSON is : {:?}&quot;, request_json);<br>  let request_struct: TempData = serde_json::from_str(request_json)?;</pre><p>Next, we need to push this struct into our DynamoDB table. I’m choosing to separate each data element into its own DynamoDB attribute instead of storing the JSON directly. We do some minor data formatting to pull out the day as a separate attribute to use as our Partition Key. The rest of the struct values convert into AttributeValues for the Dynamo DB API. Our error handling hides DynamoDB-specific error messages from the end user as an implementation detail.</p><pre>//push-temp/src/main.rs (continued)<br><br>// set up as a DynamoDB client<br>let config = aws_config::load_from_env().await;<br>let client = Client::new(&amp;config);<br><br>// build the values that are stored in the DB<br>let record_date_av = AttributeValue::S(request_struct.record_date.clone());<br>let thermostat_on_av = AttributeValue::S(request_struct.thermostat_on.to_string());<br>let temperature_av = AttributeValue::N(request_struct.temperature.to_string());<br>let thermostat_value_av = AttributeValue::N(request_struct.thermostat_value.to_string());<br>let record_day_av: AttributeValue = AttributeValue::S(request_struct.record_date[..10].to_string());<br><br>// Store our data in the DB<br>let _resp = client<br>  .put_item()<br>  .table_name(&quot;Shop_Thermostat&quot;)<br>  .item(&quot;Record_Day&quot;, record_day_av)<br>  .item(&quot;Record_Date&quot;, record_date_av)<br>  .item(&quot;Thermostat_On&quot;, thermostat_on_av)<br>  .item(&quot;Temperature&quot;, temperature_av)<br>  .item(&quot;Thermostat_Value&quot;, thermostat_value_av)<br>  .send()<br>  .await<br>  .map_err(|err| {<br>    error!(&quot;failed to put item in Shop_Thermostat, error: {}&quot;, err);<br>    FailureResponse {<br>      body: &quot;The lambda encountered an error and your message was not saved&quot;.to_owned(),<br>    }<br>  })?;<br>debug! {<br> &quot;Successfully stored item {:?}&quot;, &amp;request_struct<br> }<br>Ok(&quot;the lambda was successful&quot;.to_string())<br>}</pre><p>To deploy our custom Lambda function to AWS, we need to create an executable called “bootstrap.” We need Rust to build our executable by cross-compiling to the x86_64-unknown-linux-musl target — which is what the Lambda run time requires. I like using <a href="https://github.com/casey/just">just</a> as a command runner and created a simple justfile for the build, which runs the two commands we need to produce the executable called “bootstrap” in our local directory. I use the <a href="https://github.com/cross-rs/cross">cross tool</a> (cargo install cross), which pulls down a Docker container for the cross-compile environment. The <a href="https://docs.aws.amazon.com/sdk-for-rust/latest/dg/lambda.html">AWS SDK</a> documents alternatives to cross if you don’t want to use a local docker container. Finally, we copy the produced executable to the magic file name of “bootstrap” and store it in our project root.</p><pre>#push-temp/justfile<br>build: <br> cross build - release - target x86_64-unknown-linux-musl<br> cp target/x86_64-unknown-linux-musl/release/push_temp bootstrap</pre><p>We could manually deploy our Lambda function by zipping up the bootstrap file and uploading it through the AWS web interface. But other AWS pieces need to go around the Lambda function for everything to work. We need to set up permissions for the Lambda function to insert data into our DynamoDB tables and permissions for executing the Lambda function itself.</p><p>Recently, AWS has published a means to create a <a href="https://docs.aws.amazon.com/lambda/latest/dg/lambda-urls.html">Lambda Function URL</a> — an HTTPS endpoint directly connected to a Lambda function. For simple use cases like ours, a Lambda Function URL allows for a simpler setup and avoids having to create an API gateway endpoint. If an API gateway endpoint is important to you, I’d suggest reading <a href="https://medium.com/aws-tip/crud-operations-with-rust-on-aws-lambda-part-2-bd1feae2554b">this article</a> which includes the additional steps needed. My approach is a simplified version of the one described.</p><p>We could use the AWS console to create our Lambda Function, Function URL, and DynamoDB – but it’s not very repeatable. Instead, let’s use Terraform to define the pieces we need to have a repeatable process. It also gives us a clean way to delete everything when we want to do that. I split up the Terraform configuration into a set of files for each piece of our deployment, all located in the root of the push_temp crate. First, a variables.tf file will define a couple of shared values we’ll need:</p><pre>#push-temp/variables.tf<br><br># Input variable definitions, adjust for your needs<br>variable &quot;aws_region&quot; {<br>  description = &quot;AWS region for all resources.&quot;<br>  type = string<br>  default = &quot;us-east-2&quot;<br>}<br><br>variable &quot;push_temp_bin_path&quot; {<br>  description = &quot;The binary path for the lambda.&quot;<br>  type = string<br>  default = &quot;./bootstrap&quot;<br>}</pre><p>Then, a main.tf file sets up our environment:</p><pre>#push-temp/main.tf<br><br>terraform {<br>  required_providers {<br>    aws = {<br>      source = &quot;hashicorp/aws&quot;<br>      version = &quot;~&gt; 4.0&quot;<br>    }<br>    archive = {<br>      source = &quot;hashicorp/archive&quot;<br>      version = &quot;~&gt; 2.2.0&quot;<br>    }<br>  }<br><br>  required_version = &quot;~&gt; 1.0&quot;<br>}<br><br>provider &quot;aws&quot; {<br>  region = var.aws_region<br>}<br><br>data &quot;aws_caller_identity&quot; &quot;current&quot; {}</pre><p>Now we can set up each of the resources we need to deploy. First, we create the DynamoDB table. Note that we define the two columns we use as keys, and the rest get dynamically created as we insert data. Our keys are strings, so we use the type = “S” to define them. We initialize the table at the lowest possible resource usage, as we have a single little Raspberry Pi sending us data.</p><pre>#push-temp/dynamo.tf<br><br># aws_dynamodb_table.shop-thermostat-table:<br>resource &quot;aws_dynamodb_table&quot; &quot;shop-thermostat-table&quot; {<br>  hash_key = &quot;Record_Day&quot;<br>  name = &quot;Shop_Thermostat&quot;<br>  range_key = &quot;Record_Date&quot;<br>  billing_mode = &quot;PAY_PER_REQUEST&quot;<br>  read_capacity = 0<br>  write_capacity = 0<br>  <br>  attribute {<br>    name = &quot;Record_Day&quot;<br>    type = &quot;S&quot;<br>  }<br><br>  attribute {<br>    name = &quot;Record_Date&quot;<br>    type = &quot;S&quot;<br>  }<br>}</pre><p>Next, we can define our lambda function. We need to provide the .zip file of our executable to Terraform for the initial deployment to set up the Lambda function. I don’t want to use Terraform to deploy our executable on every application change — Terraform is not a CI/CD tool. But we need something to create the function. So after the resources are all created successfully, we will use a different method to deploy application updates.</p><p>We also set up a Lambda Function URL as the publicly reachable endpoint.</p><pre>#push-temp/lambdas.tf<br><br># Here we grab the compiled executable and use the archive_file package<br># to convert it into the .zip file we need.<br>data &quot;archive_file&quot; &quot;push_temp_lambda_archive&quot; {<br>  type = &quot;zip&quot;<br>  source_file = var.push_temp_bin_path<br>  output_path = &quot;bootstrap.zip&quot;<br>}<br><br># Here we set up an IAM role for our Lambda function<br>resource &quot;aws_iam_role&quot; &quot;push_temp_lambda_execution_role&quot; {<br>  assume_role_policy = &lt;&lt;EOF<br>  {<br>    &quot;Version&quot;: &quot;2012–10–17&quot;,<br>    &quot;Statement&quot;: [<br>    {<br>      &quot;Action&quot;: &quot;sts:AssumeRole&quot;,<br>      &quot;Principal&quot;: {<br>        &quot;Service&quot;: &quot;lambda.amazonaws.com&quot;<br>      },<br>      &quot;Effect&quot;: &quot;Allow&quot;,<br>      &quot;Sid&quot;: &quot;&quot;<br>    }<br>  ]<br>}<br>EOF<br>}<br><br># Here we attach a permission to execute a lambda function to our role<br>resource &quot;aws_iam_role_policy_attachment&quot; &quot;push_temp_lambda_execution_policy&quot; {<br>  role = aws_iam_role.push_temp_lambda_execution_role.name<br>  policy_arn = &quot;arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole&quot;<br>}<br><br># Here is the definition of our lambda function <br>resource &quot;aws_lambda_function&quot; &quot;push_temp_lambda&quot; {<br>  function_name = &quot;PushTemp&quot;<br>  source_code_hash = data.archive_file.push_temp_lambda_archive.output_base64sha256<br>  filename = data.archive_file.push_temp_lambda_archive.output_path<br>  handler = &quot;func&quot;<br>  runtime = &quot;provided&quot;<br>  <br>  # here we enable debug logging for our Rust run-time environment. We would change<br>  # this to something less verbose for production.<br> environment {<br>   variables = {<br>     &quot;RUST_LOG&quot; = &quot;debug&quot;<br>   }<br> }<br> <br> #This attaches the role defined above to this lambda function<br> role = aws_iam_role.push_temp_lambda_execution_role.arn<br>}<br><br>// Add lambda -&gt; DynamoDB policies to the lambda execution role<br>resource &quot;aws_iam_role_policy&quot; &quot;write_db_policy&quot; {<br>  name = &quot;lambda_write_db_policy&quot;<br>  role = aws_iam_role.push_temp_lambda_execution_role.name<br>  policy = &lt;&lt;EOF<br>{<br>  &quot;Version&quot;: &quot;2012–10–17&quot;,<br>  &quot;Statement&quot;: [<br>   {<br>     &quot;Sid&quot;: &quot;&quot;,<br>     &quot;Action&quot;: [<br>       &quot;dynamodb:PutItem&quot;<br>     ],<br>     &quot;Effect&quot;: &quot;Allow&quot;,<br>     &quot;Resource&quot;: &quot;arn:aws:dynamodb: :${var.aws_region}::${data.aws_caller_identity.current.account_id}:table/Shop_Thermostat&quot;<br>   }<br> ]<br>}<br>EOF<br>}<br><br>// The Lambda Function URL that allows direct access to our function<br>resource &quot;aws_lambda_function_url&quot; &quot;push_temp_function&quot; {<br>   function_name = aws_lambda_function.push_temp_lambda.function_name<br>   authorization_type = &quot;NONE&quot;<br>}</pre><p>Lastly, we create an output file so we can get the API endpoint for calling our function:</p><pre>#push-temp/output.tf<br><br># Output value definitions<br>output &quot;invoke_url&quot; {<br>  value = aws_lambda_function_url.push_temp_function.function_url<br>}</pre><p>Whew, that’s all done! A `terraform init &amp; terraform apply` will create all the stuff, upload our newly compiled function, and make it ready for testing!</p><p>We can call the external endpoint through curl, replacing &lt;endpoint&gt; below with the value that terraform outputs on the apply.</p><pre>curl -X POST https://&lt;endpoint&gt;.lambda-url.us-east-2.on.aws/ \<br>-H &#39;Content-Type: application/json&#39; \<br>-d &#39;{&quot;record_date&quot;:&quot;2022–02–03T13:22:22&quot;,&quot;thermostat_on&quot;:true,&quot;temperature&quot;:&quot;65&quot;,&quot;thermostat_value&quot;:&quot;64&quot;}&#39;</pre><p>You can use the DynamoDB console to see your new record in the database:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/624/1*rtot0Iugu4s8oeVigsbkKw.png" /></figure><p>To make application updates to the code after the initial deployment, I created a deploy target in my justfile for the commands needed to deploy an updated application. These commands rely on the AWS CLI to be installed and configured for the same region as the Lambda function.</p><pre>#push-temp/justfile (continued)<br>deploy: build<br>  cp target/x86_64-unknown-linux-musl/release/push_temp bootstrap<br>  zip bootstrap.zip bootstrap<br>  aws lambda update-function-code - function-name PushTemp - zip-file fileb://./bootstrap.zip</pre><p>Now that we have a working back-end that can accept an HTTP Post with our JSON data and persist it in DynamoDB, we can create a Rust front-end that sends that request. Our Cargo.toml in the main application again has a reference to our shared TempData crate so that we can use the shared struct.</p><pre>[dependencies]<br>temp-data = { path=&quot;temp-data&quot; }</pre><p>I created a function store_temp_data() to use whenever new data is available within the Rust application. I pass in the data and the endpoint URL, which is in the run-time configuration elsewhere. I’m using the reqwest crate for the base HTTP client. Our function starts by initializing the client and building the request structure TempData we saw earlier. We also grab the current time and convert it to the RFC3339 format.</p><pre>//thermostat-pi/src/send_temp.rs<br><br>use reqwest;<br>use reqwest::Error;<br>use time::format_description::well_known::Rfc3339;<br>use time::macros::offset;<br>use time::OffsetDateTime;<br><br>extern crate temp_data;<br>use temp_data::TempData;<br><br>pub async fn store_temp_data(<br>  thermostat_on: bool, <br>  current_temp: f32,<br>  thermostat_value: i16,<br>  aws_url: &amp;str,<br>) -&gt; Result&lt;(), Error&gt; {<br>  let client = reqwest::Client::new();<br>  <br>  // Get the current time, offset to my timezone<br>  let now = OffsetDateTime::now_utc().to_offset(offset!(-6));<br>  let now = now.format(&amp;Rfc3339).unwrap();<br><br>  let body = TempData {<br>    record_date: now,<br>    thermostat_on: thermostat_on,<br>    temperature: current_temp<br>    thermostat_value: thermostat_value<br>  };</pre><p>Next, we send the request to our endpoint, serializing it into JSON along the way, and handle the response. I’m choosing to log the error and return OK on an error, as this is a non-critical function for our application.</p><pre>//thermostat-pi/src/send_temp.rs (continued) <br><br>  let response = client<br>    .post(aws_url)<br>    .json(&amp;body)<br>    .send()<br>    .await;<br><br>  match response {<br>    Ok(r) =&gt; {<br>      tracing::debug!(&quot;response: {:?}&quot;, r);<br>    }<br>    Err(e) =&gt; {<br>      tracing::error!(&quot;Error sending to AWS, {}&quot;, e);<br>    }<br>  }<br><br>  Ok(())<br>}</pre><p>And that’s it! For the five things we set out to accomplish, here are our key takeaways:</p><ol><li>We are defining the TempData struct in a separate crate (directory) with its own Cargo.toml giving us a common referenceable structure for the API between our client and server applications. Utilizing the struct to define a JSON-based interface between the client and server, and using serde to serialize and deserialize our TempData structure on either end, is simple to set up and keep in sync across our projects.</li><li>The AWS Rust SDK provides easy-to-use interfaces for Rust for Lambda definition and DynamoDB access. Rust makes for a great Lambda execution environment with its speed and low memory footprint.</li><li>Terraform works great to build out all of the AWS components we need and set up the permissions pieces required to glue everything together.</li><li>Using the AWS CLI is an easy way to update our Lambda executable on demand.</li><li>The reqwest crate gives us a straightforward means to send HTTP requests for our client application.</li></ol><p>I hope you find this useful on your Rust journey! If you have any improvement ideas, please provide feedback in the comments.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=bea841d47cca" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/rust-lambda-and-dynamodb-bea841d47cca">Rust, Lambda, and DynamoDB</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Magic of Borrow Checkers in Rust]]></title>
            <link>https://medium.com/better-programming/the-magic-of-borrow-checkers-in-rust-238a2a97bff2?source=rss-8b3e41d73ef4------2</link>
            <guid isPermaLink="false">https://medium.com/p/238a2a97bff2</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[coding]]></category>
            <dc:creator><![CDATA[Michael Hentges]]></dc:creator>
            <pubDate>Wed, 09 Nov 2022 18:43:18 GMT</pubDate>
            <atom:updated>2022-11-09T20:04:06.880Z</atom:updated>
            <content:encoded><![CDATA[<h3>The Magic of the Rust Borrow Checker</h3><h4>Learn what makes the Rust programming language so unique</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*6i93fF841B1nCfnF" /><figcaption>Photo by <a href="https://unsplash.com/@jantined?utm_source=medium&amp;utm_medium=referral">Jantine Doornbos</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>When I first tripped across Rust, it was through an article touting Rust’s election as the “most loved programming language” for several years in Stack Overflow’s yearly <a href="https://survey.stackoverflow.co/2022/#technology-most-loved-dreaded-and-wanted">survey of the most loved programming languages</a>.</p><p>The high-level language description was interesting — compiled, no virtual machine, efficient, fast, and safe. I’ll admit a bias towards compiled and type-safe languages. I grew up on C, have taught C, C++, and Java, and have used Java for most of my professional career. Very early in my research, I ran across a bunch of articles of the type “Why Rust is better than &lt;insert programming language here&gt;” and “Why you should learn Rust.”</p><p>They all seem to have come from the same root source — and mostly cover points that any statically typed language that doesn’t run on a virtual machine runtime would have. “Blazingly fast” “catches variable type mismatch at compile time” are examples.</p><p>What is missing is an explanation of what is unique to Rust that other languages do not have. I want to tackle the first reason in this article — the Rust Borrow Checker.</p><p>The inventors of Rust benefit from years of practical experience with other programming languages. They have designed a set of features in the language and associated tooling that address many of the pain points facing application developers. First up is memory management. From the beginning, programs have dealt with how to use memory safely, and the Rust Borrow Checker is a novel approach to solving this problem.</p><h3>Some history</h3><p>First, let me review some history of how programming languages have tried to tackle this problem. In the beginning, there was C, and the low-level and often-cursed “malloc()” and “free().” C is a programming language that gives developers complete control of their environment — it is a small step up from assembly language.</p><p>Developers get to fully manage memory access for their application and how to give allocated memory back to the operating system. This low-level control takes a lot of careful planning. But, it was the source of many runtime errors, through either memory leaks (not letting go of memory) or crashes (freeing the same pointer twice or using memory after it is released are common problems). It is also a source of security issues — reading previously freed memory for data you’re not supposed to have is a possible exploit.</p><p>Here’s an example program that demonstrates what the compiler allows but is incorrect:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0ceaf56af986683de774e68c28056548/href">https://medium.com/media/0ceaf56af986683de774e68c28056548/href</a></iframe><p>C++ enabled better control over memory allocation. Classes had constructors that would allocate memory for their objects, and destructors freed that memory. Smart Pointers also were created that helped with automatically freeing memory when variables fell out of scope. But the “foot guns” of low-level pointers and references made errors challenging to eliminate. You were OK if you followed the rules, but if you weren’t aware of the rules, the compiler wasn’t of any help. The hidden complexity inside objects also made it difficult to follow the rules and predict how your application would perform.</p><h3>Garbage Collection</h3><p>Programming languages turned to garbage collection as a means to solve these problems. Garbage collection-based programs can grab whatever memory they need from a runtime environment. In the background, the garbage collection process will release memory back to the operating system once the program is no longer using it. Scripting languages that rely on interpreting source code at run time, including Basic, Python, and JavaScript, use this approach in the run time environment of their interpreters.</p><p>Java was one of the first compiled languages that targeted running within a runtime (the JVM) that performed garbage collection. New programming languages, including Golang (Go) and Dart, utilize garbage collection. These environments are productive for developers — it makes managing memory much simpler. Garbage collectors have gotten very sophisticated, and many application types work well in this environment.</p><p>But garbage collection imposes limitations. The overhead imposed by the garbage collector impacts the application’s runtime behavior. Pauses in the application must be scheduled, which makes garbage-collected languages unsuitable for real-time programming. The runtime that goes along with the environment is also typically heavyweight, which gets in the way when running in the small environments of embedded and IoT devices — or containerized microservices.</p><p>Here’s a quick example of a similar Java application to our C version. It shows how garbage collection is useful:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ba16f2d7d1febc0dec05706b2a94e8e7/href">https://medium.com/media/ba16f2d7d1febc0dec05706b2a94e8e7/href</a></iframe><p>Everything compiles, but we get consistent crashes at run time instead of undefined runtime behavior; that’s good and bad. Predictable behavior is good, but the Java “NullPointerException” runtime error has crashed applications in production more times than anyone would want to admit.</p><p>When we comment out the bad lines, we now see a side effect — the new variable did not receive a completely new version of the array; it just “pointed” to the original. When we modified the new variable, the original one had its contents modified out from under it. In this trivial example, we can follow what is happening here, but if that modification occurred deep within a nested library function, we could easily be surprised.</p><p>But, our memory management is more straightforward. We get to request the memory space we need at run time and don’t have to deal with letting it go. The ease of use is why garbage collection is widespread, and mainstream languages like JavaScript, Python, Go, and Dart are using it today.</p><h3>What Rust Does Differently</h3><p>Rust’s approach to memory management enables the ease of memory management of a garbage collection environment without the runtime environment overhead or associated performance penalty. It also solves the unexpected side effect problems we saw in our Java example.</p><p>A smart pointer-style mechanism is a key, but instead of relying on programmers to follow usage rules (like C++), the compiler enforces access rules. “Fighting the borrow checker” is one of the first things new Rust developers figure out how to do, but once mastered, it enables developers to manage their memory usage automatically.</p><p>Along with a productive development environment, Rust’s borrow checker eliminates a whole class of programming errors related to memory management. Surveys have demonstrated that many of the security vulnerabilities in applications are due to a program’s inability to protect access to allocated memory. Rust programs run fast, enable developers to be efficient, are more secure, and reduce runtime crashes.</p><p>Let’s look at a Rust application that mimics our examples:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/af61af24e7466ad9370faa2b5986cb5c/href">https://medium.com/media/af61af24e7466ad9370faa2b5986cb5c/href</a></iframe><p>We again have a function that allocates a block of memory on the heap using Rust’s Vector type. A Vector is a contiguous block of memory that is dynamically sized, accessed like an array, and stored on the heap. By contrast, line 8 allocates an array, which is fixed in size at compile time and allocates on the stack. Since we know the array size at compile time, we get a compiler warning on line 11. Rust knows we’re going past the end of the array.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3771157593d4c8256091e34a0bd28573/href">https://medium.com/media/3771157593d4c8256091e34a0bd28573/href</a></iframe><p>The Rust compiler is excellent at giving us meaningful error information. On line 15, the vector is “moved” to a new home — it no longer exists at my_vector. Rust keeps track of the allocated memory but ensures that only one variable “owns” the memory and can make changes.</p><p>We can assign a variable to our vector if we state it is an immutable variable. We can only “read” the values through this new variable, not write to them. Rust enforces a rule that there can only be one “writer” / “owner” at a time — that way, it prevents all “whoever saves last wins” errors, the unexpected side effect issues we saw in our Java example. It can also keep track of the variables using the allocated memory to ensure it gets freed when all of the variables using the memory fall out of scope.</p><p>Here’s our example program with an immutable variable pointing to our vector:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0a8ce3cfe7cabb23d1ac10f87fd9e2c4/href">https://medium.com/media/0a8ce3cfe7cabb23d1ac10f87fd9e2c4/href</a></iframe><h3>The Magic of Rust’s Borrow Checker Is…</h3><ol><li>Rust ensures ownership of any memory (heap or stack) is in one place. Only the owner of the data can manipulate it. Multiple read-only access is allowed.</li><li>Rust keeps track of the memory variables used and automatically frees up memory when all the variables using that memory are out of scope — whether on the stack (our array example) or the heap (our Vector example).</li><li>Bounds checking is enforced either at compile time (for arrays, which have their sizes determined at compile time) or run time (vectors, which have their sizes set at run time).</li><li>These rules eliminate a whole class of errors and security vulnerabilities. We can’t write to memory after a free() — Rust keeps memory around automatically for us as long as anything has access. These rules are all checked at compile time. We don’t have to worry about letting go of memory — Rust takes care of that. Boundary checks also prevent us from writing outside of the memory space allocated to us.</li><li>Most of this happens at compile time — we do not have a runtime garbage collector that has to run alongside us, and there are no interruptions to the program execution to allow a garbage collector to interrogate memory usage. A common Rust saying is: “if it compiles, it will work.”</li></ol><p>Even better, how the borrow checker works enable multiple threads to use allocated memory safely. When programs move into multiple threads of execution, the opportunity for error expands exponentially. “Fearless concurrency” — using data safely across multiple threads automatically — is a significant benefit of Rust’s borrow checker.</p><p>Rust’s memory management and the borrow checker are two main reasons Rust is different. You can access memory at a low level with complete control, without a garbage collector runtime or the risks of a runtime error blowing up your program unexpectedly.</p><p>This feature makes Rust great for low-level systems, embedded, or real-time applications where efficiency is necessary. It also enables Rust to be used for higher-level applications such as web service publishing, as the “memory automatically frees” environment is productive for developers.</p><p>But that’s not all.</p><p>Stay tuned for another feature of Rust that makes it great!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=238a2a97bff2" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/the-magic-of-borrow-checkers-in-rust-238a2a97bff2">The Magic of Borrow Checkers in Rust</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Implementing Multi-threaded Shared Memory in Rust]]></title>
            <link>https://medium.com/better-programming/easy-multi-threaded-shared-memory-in-rust-57344e9e8b97?source=rss-8b3e41d73ef4------2</link>
            <guid isPermaLink="false">https://medium.com/p/57344e9e8b97</guid>
            <category><![CDATA[design-patterns]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Michael Hentges]]></dc:creator>
            <pubDate>Mon, 31 Oct 2022 13:01:34 GMT</pubDate>
            <atom:updated>2022-11-01T19:51:26.258Z</atom:updated>
            <content:encoded><![CDATA[<h3>Implementing Multi-Threaded Shared Memory in Rust</h3><h4>Making shared data design pattern work</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*OC6GlEDwfqS1xr5f8PgCOQ.jpeg" /></figure><p>This article is the third in a series related to building a wireless thermostat in Rust, running on a Raspberry Pi — although this one has very little to do with the Raspberry Pi and is more relevant to any multi-threaded application. While creating my application, I implemented a simple and effective design for controlling multi-threaded access to shared memory. You can find the previous articles here:</p><ul><li>The first article, <a href="https://medium.com/p/45a5d35196cf">Raspberry Pi Wireless Thermostat in Rust</a></li><li>The second one,<a href="https://medium.com/p/e30fcb233fef"> Cross Compiling Made Easy</a>.</li></ul><p>I also have a few code snippets below, but you can find the entire code repository on <a href="https://github.com/mikehentges/thermostat-pi">GitHub</a>.</p><h3>First, the Backstory</h3><p>When designing a multi-threaded application, one of the core considerations is how the different threads share data. There’s always something that the threads share — you’re spinning off threads to work on a shared problem, after all. If nothing else, configuration data or database connection pools have to be shared. There are two main ways of approaching this problem:</p><ol><li>Message Passing — channels between threads allow data to be sent and received between them. Message passing is the mechanism that Golang prefers, as the Golang documentation states: “Do not communicate by sharing memory; instead, share memory by communicating.” Rust supports creating channels; you can find more information in the Rust Book’s chapter on <a href="https://doc.rust-lang.org/book/ch16-02-message-passing.html">message passing</a>.</li><li>Shared-State — a set of data identified as a shared state. Each thread access a shared data area through thread-safe protection mechanisms — typically a Mutex. Locks on the data allow you to access the data safely, reading or updating the data as needed, and then the lock is released. Only one thread can read or write the data at a time. The Rust Book’s <a href="https://doc.rust-lang.org/book/ch16-03-shared-state.html">shared state</a> section describes these mechanisms.</li></ol><p>Both approaches work, and Rust has good support for both mechanisms in the standard library. To choose between them, consider the following:</p><ol><li>What are the access patterns? Will threads mainly read data, mostly write data, or both?</li><li>How will deadlock be avoided? Avoiding deadlock is a primary concern for multithreaded applications — nothing is worse than having a bunch of threads available to do work, and they are all stuck waiting for the other ones to get out of the way.</li><li>Performance. While over-engineering for performance is a common fault, having some idea of the performance requirements of your solution is essential. I’m a big fan of the rule: keep the solution as simple as possible, but no simpler.</li></ol><p>I decided on a shared state approach for the application I am creating. This decision also implied that my worker threads would work by polling — checking on things periodically instead of receiving an outside message. Here is what led me to that conclusion:</p><ol><li>I have three main threads. One is a web interface that allows for reading/writing of the shared state (get temperature, get thermostat setting, set thermostat setting). The web interface is, by definition, a creator of unscheduled activity — things come in based on an external client’s action and are not under the application’s control. <br>Second, a pair of background worker threads react to the application’s environment. The first reads the temperature sensor, and the second calculates whether the thermostat should be set on or off and controls the physical relay to make that happen. The thermostat is in a separate thread, as it could turn on/off when the temperature crosses a threshold or we receive a new thermostat setting from the web interface.</li><li>I didn’t have to worry about contention with only three threads (assuming a single external client on the web interface). A simple shared state approach wouldn’t run into issues based on too many threads trying to access locks simultaneously.</li><li>The logic for determining the thermostat’s state has a time component. We don’t want frequent, short bursts of on/off, which thrash the furnace. We enforce a minimum amount of time that the thermostat will be on or off before switching to another state. Accurately reacting to a message (the temperature reading or thermostat value changes) requires knowing how long it’s been since the thermostat changed.</li></ol><p>A shared state made sense based on these requirements. My design enforces a low thread count, so contention was not an issue (a common problem with shared state approaches). I need to keep track of time, so some state data was always required. Implementing two different state data mechanisms in the same application would be added complexity. With each thread doing its own thing and getting/setting shared state independently, we isolate each thread from the others. This choice led to a more straightforward overall application design.</p><p>But, as I reviewed the Rust Book’s content on <a href="https://doc.rust-lang.org/book/ch16-03-shared-state.html">shared-state</a>,<br>the complexity of managing access from each thread seemed daunting. Plus, sprinkling thread-locking code in the application made it a mess — my nice single-purpose functions now had Mutex locking logic. Also, when accessing data through a Mutex lock dealing with Rust’s borrow checker across threads proved challenging.</p><h3>A Simpler Approach</h3><p>To make this manageable, I used an approach I’ve used before on C++ projects — encapsulating the shared data into a separate class and pushing all Mutex logic into the class. Rust doesn’t have classes, but some structs did the trick.</p><p>I created a struct to hold all the shared state data in a single place. Then I could deal with a single reference to this struct instead of managing each data element independently.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b9ddfbb49d27d38a20ee106c07e93ffc/href">https://medium.com/media/b9ddfbb49d27d38a20ee106c07e93ffc/href</a></iframe><p>Then we define a struct that holds an Arc pointer — an Atomic Reference Count pointer to a Mutex that guards our shared data struct. We use this struct to control access to/from our shared data.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/db73df9248a1a27d366322380fc710d5/href">https://medium.com/media/db73df9248a1a27d366322380fc710d5/href</a></iframe><p>We will make many copies of this pointer — they all point to our Mutex, which is how we gain access to our shared memory space. We do this by customizing the Clone() method for AccessSharedData — it looks like the following:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8a577caa72dac93c0ae0b63992049c95/href">https://medium.com/media/8a577caa72dac93c0ae0b63992049c95/href</a></iframe><p>To use this, we first create an instance of our SharedData struct (the simple .new() method isn’t shown above but is straightforward).</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/9d872d49a59fc17f8633a0aeae0f59d4/href">https://medium.com/media/9d872d49a59fc17f8633a0aeae0f59d4/href</a></iframe><p>Then, we initialize an instance of AccessSharedData.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3e1124f1e03e0df29856fa1f9a3c5b23/href">https://medium.com/media/3e1124f1e03e0df29856fa1f9a3c5b23/href</a></iframe><p>We then give each thread we spawn() a cloned copy of the AccessSharedData struct. The clone call creates a copy of the Arc pointer, which we then move into the new thread. A similar method passes in a clone of the AccessSharedData struct to the actix_web HttpServer::new() method, so it is also available in HTTP client handlers.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/fdb94540ae09357d299d68fffd306033/href">https://medium.com/media/fdb94540ae09357d299d68fffd306033/href</a></iframe><p>Now that everyone has a copy of the Arc pointer, we need to create a means to use it to read/write our shared data struct. A simple set of getters/setters for each member of our struct handles the task of acquiring a lock, getting/setting the data, and automatically releasing the lock. By putting this logic surrounding the get/set and utilizing the end of the method as a scope boundary to force the lock to be released, we gain control over the access to our data.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/d66a3e49e1d07d9e44f5af719028a84f/href">https://medium.com/media/d66a3e49e1d07d9e44f5af719028a84f/href</a></iframe><p>When each get/set function returns, the lock is released. This approach also means it is impossible to deadlock around access to the structure — everything is locked and released immediately and prohibits anything else from getting in the way. With all of our locking mechanisms in place, using the shared data in the rest of our application is trivial.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/45a187125152682458d8ed2b9406d785/href">https://medium.com/media/45a187125152682458d8ed2b9406d785/href</a></iframe><h3>In Summary</h3><p>This strategy is encapsulation at its finest. Application code uses straightforward calls to methods to get/set the shared data elements whenever needed. Behind the scenes, we manage the Mutex’s locking for that access to be thread-safe and safe from deadlocks. And Rust’s borrow checker is extremely happy that we aren’t allowing direct shared mutable references to our shared data. You could easily extend this design pattern to implement a more robust access pattern.</p><p>Putting locking logic inside getters/setters enables our shared data struct to hide the complexities of multi-threaded access — much like the standard library does for thread-safe collections. Using clones of an Arc makes it possible to acquire a handle to the shared memory struct in each thread. These two concepts are the core ideas that make our easy shared data design pattern work.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=57344e9e8b97" width="1" height="1" alt=""><hr><p><a href="https://medium.com/better-programming/easy-multi-threaded-shared-memory-in-rust-57344e9e8b97">Implementing Multi-threaded Shared Memory in Rust</a> was originally published in <a href="https://betterprogramming.pub">Better Programming</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Rust Cross Compiling Made Easy]]></title>
            <link>https://medium.com/@mikehentges65/rust-cross-compiling-made-easy-e30fcb233fef?source=rss-8b3e41d73ef4------2</link>
            <guid isPermaLink="false">https://medium.com/p/e30fcb233fef</guid>
            <category><![CDATA[cross-compile]]></category>
            <category><![CDATA[just]]></category>
            <category><![CDATA[raspberry-pi]]></category>
            <category><![CDATA[rust]]></category>
            <dc:creator><![CDATA[Michael Hentges]]></dc:creator>
            <pubDate>Mon, 17 Oct 2022 19:10:15 GMT</pubDate>
            <atom:updated>2022-10-17T19:12:37.916Z</atom:updated>
            <content:encoded><![CDATA[<p>This article is the second installment of my series on building a wireless thermostat in Rust for the Raspberry Pi. You can find the <a href="https://medium.com/p/45a5d35196cf">beginning of the series here</a>. All source code for the project is <a href="https://github.com/mikehentges/thermostat-pi">located here</a>.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*R731j0ZGJL-K9zMP" /><figcaption>Photo by <a href="https://unsplash.com/@_louisreed?utm_source=medium&amp;utm_medium=referral">Louis Reed</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>As I approached the task of building a native Rust executable for the Raspberry Pi, one of the first things I had to tackle was establishing a cross-compiling development environment. The Raspberry Pi runs a flavor of Unix, but we need to compile executables for the Pi’s ARM processor.</p><p>My first attempt followed the process documented in the Cross-compilation chapter of <a href="https://rust-lang.github.io/rustup/cross-compilation.html">The Rustup Book</a>. I had to find the appropriate target for a Raspberry Pi Zero (my target): arm-unknown-linux-gnueabihf. This target will be slightly different if you want to target a Pi 3 or Pi 4 — then you can use target=armv7-unknown-linux-gnueabihf. The Pi Zero runs a v6 version of the ARM processor, larger Pi’s use a v7 (or higher) version.</p><p>Cross-compiling sounded easy, and following the Rustup Book’s directions added cross-compiling to my environment:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/00ae7126174b5cfdfb4e1fd828ba6c70/href">https://medium.com/media/00ae7126174b5cfdfb4e1fd828ba6c70/href</a></iframe><p>But that doesn’t work out of the box. Even the documentation warns you: <em>”Note that rustup target add only installs the Rust standard library for a given target. There are typically other tools necessary to cross-compile, particularly a linker.”</em> Then, my application used SSL — and I also needed a way to cross-compile the openssl library for the Pi. I build under WSL2 (Ubuntu) on a Windows machine — getting this set up would take some effort.</p><p>Fortunately, there’s an easier way! The <a href="https://github.com/rust-embedded/wg#the-tools-team">Rust Embedded Devices Tools Team</a> publishes a set of Docker images that contain a complete toolchain for a large number of cross-compile targets. Even better, they created a “ cross “ tool that automates the entire process of launching a suitable Docker container, getting your code attached to the container, and running the compile process. Incremental builds are fully supported, so cross-compiling does not take much longer than native compiling.</p><p>To set this up, you first to install the “cross” tool:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7f04a264cec4950d55af7fef4d95abc6/href">https://medium.com/media/7f04a264cec4950d55af7fef4d95abc6/href</a></iframe><p>Then, compiling any project for a new target is as simple as substituting “cross” for “cargo” in your command. I also had to pass a features flag to get the OpenSSL library built from source for the target platform:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/98d3befafce3953632fb0e49411e908d/href">https://medium.com/media/98d3befafce3953632fb0e49411e908d/href</a></iframe><p>That command will build a release executable at ./target/arm-unknown-linux-gnueabihf/release/thermostat-pi (my project is “thermostat-pi”).</p><p>For another part of the project, I needed to cross-compile to x86_64-unknown-linux-musl to create an AWS Lambda using Rust. The <a href="https://docs.aws.amazon.com/sdk-for-rust/latest/dg/lambda.html">AWS Rust SDK</a> documents the steps and includes the “Container approach” that uses cross:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/183428a37bdc9485ae752e00a0204151/href">https://medium.com/media/183428a37bdc9485ae752e00a0204151/href</a></iframe><p>You can find the lambda project in the ./push_temp folder in the <a href="https://github.com/mikehentges/thermostat-pi/tree/main/push-temp">source repository</a>.</p><p>All these build commands are too long for me to remember. Command-line history helps a ton, but returning to the project after an extended break can mean looking up the build commands again. Instead, I recently found <a href="https://github.com/casey/just">just</a>, a lighter-weight version of make that is purpose-built as a command runner. You can create a justfile, using syntax similar to make’s Makefile, to define a set of commands. Dependencies are supported, allowing you to bundle together different commands as needed. For the ./push_temp project, its very simple justfile looks like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0b66587fb5fb1929d2055b3cdfe59622/href">https://medium.com/media/0b66587fb5fb1929d2055b3cdfe59622/href</a></iframe><p>This setup allows a simple just build command — which I can remember! The test target sends a sample transaction to my lambda function — again, keeping in a central place a longer command that I can reuse as needed.</p><p>I also created a justfile to run the main project&#39;s cross-compile and automate the executable deployment to the Raspberry Pi. The justfile I created looks like this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f85128dad4b4293686b292d307c0925e/href">https://medium.com/media/f85128dad4b4293686b292d307c0925e/href</a></iframe><p>This script relies on SSH being available between your host machine and the PI, configured with <a href="https://www.digitalocean.com/community/tutorials/how-to-configure-ssh-key-based-authentication-on-a-linux-server">public key authentication</a>.</p><p>Using the Cross tools is the secret sauce that makes cross-compiling for Rust easy. Whether you use Windows, macOS, Linux, or rent a dev box in the cloud, you can set up the cross-compiling environment with a few simple commands. Then, use justto provide automation to allow for easy-to-execute build and deploy steps.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e30fcb233fef" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating a new blog with Jekyll and GitHub Pages]]></title>
            <link>https://medium.com/@mikehentges65/creating-a-new-blog-with-jekyll-and-github-pages-f75a9a694d4b?source=rss-8b3e41d73ef4------2</link>
            <guid isPermaLink="false">https://medium.com/p/f75a9a694d4b</guid>
            <category><![CDATA[blog]]></category>
            <category><![CDATA[jekyll]]></category>
            <category><![CDATA[github-pages]]></category>
            <category><![CDATA[jamstack]]></category>
            <dc:creator><![CDATA[Michael Hentges]]></dc:creator>
            <pubDate>Sat, 08 Oct 2022 21:31:53 GMT</pubDate>
            <atom:updated>2022-10-09T20:29:14.734Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*sQrYNitGbXERkMUvK0zawg.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/@mariodobelmann?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Mario Dobelmann</a> on <a href="https://unsplash.com/s/photos/journey?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>Even though I’ve been in software development for almost my whole career, I’ve never had the chance to build a website from scratch. I’ve created a WordPress site before — but that hardly counts as programming!</p><p><strong>Getting Started on my Journey</strong></p><p>So when it came time to create a personal blog, <a href="https://mhentges.com">mhentges.com</a>, I dove in head-first and learned a bunch of new things. Initially, I ran across <a href="http://jamstack.org">Jamstack</a> as a new way of building static sites. Jamstack seemed like a much better technology stack for what I wanted to create than WordPress.</p><p>At first, I shied away from <a href="https://en.wikipedia.org/wiki/Markdown">markdown</a> for content creation. While I had used <a href="https://slack.com/">Slack</a> for instant messaging, which utilizes markdown, I wasn’t sure I would have enough formatting control for a blog site. This reluctance led me to the idea of a headless CRM for content creation and a site generator to pull from the CRM to create the pages.</p><p><strong>The first attempt</strong></p><p>My initial technology stack utilized <a href="https://ghost.org/">Ghost</a> for CRM and <a href="https://www.gatsbyjs.com/">Gatsby</a> as the site generator. Ghost’s page editor is great — easy to use and powerful. But, the connection between Ghost and Gatsby was a little clunky. I also struggled with the formatting and structure I wanted. I didn’t have a solid background in HTML and CSS, which made the jump to learning the <a href="https://reactjs.org">React</a>-based Gatsby toolset difficult for me.</p><p>After spinning my wheels for a while, I eventually just pulled out Gatsby and went with a pure Ghost site. Controlling the layout was more manageable, and I could make a structure I was happy with — pushing the site through Gatsby didn’t add much value.</p><p>But now I needed a server up 24x7. I spun up a Ghost instance on <a href="https://aws.amazon.com/lightsail/">AWS/Lightsail</a>, moved my domain to <a href="https://aws.amazon.com/route53/">AWS/Route 53</a>, and connected the pieces. Everything worked great, but I had monthly bills and a more complex stack than necessary for a simple site like this blog.</p><p><strong>The second try — progress!</strong></p><p>After some more research, I ran across <a href="https://pages.github.com">GitHub Pages</a>. It offers free hosting within extremely generous usage patterns. GitHub Pages uses <a href="https://jekyllrb.com">Jekyll</a> as a site generator — getting me back to a more Jamstack approach to building the site. As a developer, I already have a <a href="https://github.com">GitHub</a> account and am familiar with <a href="https://git-scm.com">git</a>, so pushing updates to Github to update the website was reasonable.</p><p>But I wanted a layout that wasn’t immediately available as a Jekyll theme. So, it was time to fix my lack of depth in HTML and CSS and design a site! Using the layout of my prior Ghost site as a template, I eventually figured out the right combination of HTML, CSS, Jekyll templates, and a little Javascript to put the site together. You can find all the pieces I put together in my site’s repository <a href="https://github.com/mikehentges/mikehentges.github.io">here</a>.</p><p>In the end, markdown is more than adequate as a content creation tool. Once I figured out how the Jekyll templates worked, I could easily use CSS to format blog entries and pages the way I wanted. I can run the whole site locally under Jekyll, so iterative development is easy. And the automatic push to Github to publish the site works great.</p><p><strong>Some Jekyll tips/tricks</strong></p><p>I did have to solve a few problems before I had everything working. I wanted a dynamic navigation menu that showed categories of posts on the site. I didn’t want to have to re-code the Html for the menu when categories changed and instead drove it dynamically out of a data file. Jekyll has a great set of available plug-ins for doing different things (<a href="https://github.com/planetjekyll/awesome-jekyll-plugins">Awesome Jekyll Plugins</a>). I found the <a href="https://github.com/ashmaroli/jekyll-data">Jekyll Data</a> plug-in for reading data files and using them dynamically in templates. I created a straightforward categories.yml file and used it for the main navigation menu.</p><p>Everything worked great locally, but I ran into problems when I pushed it to GitHub. GitHub Pages runs in a safe mode and has a set of allowed plug-ins — it does not natively support the Jekyll Data plug-in. So when I pushed my site to GitHub, it wouldn’t generate the static site correctly. Fortunately, a custom GitHub action, <a href="https://github.com/jeffreytse/jekyll-deploy-action">Jekyll Deploy Action</a>, solves this problem. The Jekyll Deploy Action allows you to set up a custom GitHub Actions build environment with Jekyll and any plug-ins you’d like to add. I added the right ./.github/workflows/build-jekyll.yml file to trigger the GitHub Actions on an update to the main branch. So now the sequence is:</p><ol><li>I commit my new/changed markup files. git commit -m &#39;some new changes…&#39;</li><li>I push the changes to GitHub. git push</li><li>A GitHub Action triggers, creating a Jekyll run-time environment with the plug-ins I want/need all installed.</li><li>A set of generated static files (.html, .css, .js) and any file artifacts are put in the repository’s gh-pages branch by the GitHub Action.</li><li>GitHub pages deploy the generated files, and the updated site is active!</li></ol><p>Jekyll uses the liquid template language <a href="https://shopify.github.io/liquid/">shopify.github.io/liquid</a> to drive the data substitution needed to create the static files from data collection and markup files. Mainly it involves simple data substitution, using handlebars to place data elements within the Html files, {{site.title}}for example. Programmatic control is also available, so you can iterate over a collection to create things like a list of posts. My home page has three cards for the most recent posts on the site. To make each one, I had to find the correct syntax to iterate over the posts – but only three times. The syntax to do that is:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2d3ebeae886e3086aca87a969677bedc/href">https://medium.com/media/2d3ebeae886e3086aca87a969677bedc/href</a></iframe><p><strong>End result</strong></p><p>I now have a very lightweight and fast-loading site. Everything is under my control to update or change, and creating new content is straightforward. Learning Jekyll, HTML, and CSS has been a nice side-effect of the journey, and I think I’m in a better spot to go back and tackle React one day.</p><p>If you are contemplating putting together a blog or are working towards creating a Jekyll site, I hope the information here is helpful.</p><p>Website: <a href="https://mhentges.com">mhentges.com</a></p><p>Github repository: <a href="https://github.com/mikehentges/mikehentges.github.io">mikehentges.github.io</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f75a9a694d4b" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Raspberry Pi Wireless Thermostat — in Rust]]></title>
            <link>https://medium.com/@mikehentges65/raspberry-pi-wireless-thermostat-in-rust-45a5d35196cf?source=rss-8b3e41d73ef4------2</link>
            <guid isPermaLink="false">https://medium.com/p/45a5d35196cf</guid>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[learning-to-code]]></category>
            <category><![CDATA[raspberry-pi]]></category>
            <dc:creator><![CDATA[Michael Hentges]]></dc:creator>
            <pubDate>Tue, 04 Oct 2022 17:50:58 GMT</pubDate>
            <atom:updated>2022-10-04T17:50:58.302Z</atom:updated>
            <content:encoded><![CDATA[<h3>Raspberry Pi Wireless Thermostat — in Rust</h3><figure><img alt="Raspberry Pi" src="https://cdn-images-1.medium.com/max/1024/0*1M3cYHAuBBFKE40R" /><figcaption>Photo by <a href="https://unsplash.com/@jainath?utm_source=medium&amp;utm_medium=referral">Jainath Ponnala</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>I recently stepped away from the full-time work rat race and found myself with some time on my hands. Being a life-long technologist, I naturally started looking into what new things have come along that I didn’t have time for when consumed by a full-time job.</p><p><a href="https://www.rust-lang.org/">Rust</a> popped up on a short list of new technologies that looked interesting. I started programming in C/C++ on DOS/Windows/Mac/Unix many moons ago — so a new statically typed compiled language that is heralded as the “new C/C++” was a natural choice. Going through the <a href="https://doc.rust-lang.org/book/">Rust Book</a>, I absorbed the concepts pretty quickly — but found myself in that “what next” stage. This is always the most challenging stage when learning new technology — you understand the basics but don’t know where to start to build something “real.”</p><p>In search of a solution to this problem, I ran across an excellent book by Luca Palmieri: <a href="https://www.zero2prod.com/">Zero to Production in Rust</a>. The book walks through the building of a non-trivial, production-quality web application in Rust. Many useful concepts are built out and illustrated, including:</p><ul><li>Building a production-quality web server application in Rust.</li><li>Telemetry — getting traces/logs distributed to a central location and managing error handling across several different API stacks.</li><li>Implementing end-user authentication — production-grade user authentication, including encryption and protection from common hacking methods.</li><li>SQL interaction — Postgres, in this case, including migrations.</li><li>External API usage — calling external REST-based services.</li><li>HTML templates — producing web pages for end-user interaction from a server.</li><li>Session management — Redis in this case, but a strategy for keeping track of end-user state.</li></ul><p>After working through the book, I felt I had a decent handle on how this stuff works. As a bonus, I haven’t done a lot of production-level web server development in other languages — and seeing the normal challenges in this environment solved was interesting. But I’ve never claimed to be proficient in a tool until I’ve used it to build something. About this time, an outside inspiration popped up — I wanted a way to control a thermostat in a detached workshop remotely. The heater for my workshop is a simple 2-wire control — not nearly as complicated as a home furnace. A Google Nest is overkill, I’ve done some programming for Arduino and Raspberry Pi devices in the past, so this project was born.</p><p>In laying out the architecture for my new project, I decided on the following initial scope:</p><ol><li>I’m only worried about heating — so a single control and simplified logic for either turning on the heat or turning it off.</li><li>For the main control platform, the Raspberry Pi is an easy choice. It runs a Unix OS, has Wi-Fi networking support, and has easy access to the physical devices needed for temperature measurement and relays to work as a thermostat.</li><li>I would use Rust as the development language and take advantage of the following Rust features:<br>- Cross-compile to a native executable (ARM processor).<br>- Use the Actix-Web framework to expose REST interfaces for remotely setting the thermostat and retrieving the current temperature.<br>- Utilize the Tokio run time for a multi-threaded application that can read temperatures, decide whether to turn the thermostat on or off, receive commands to set the thermostat, and send data externally.<br>- Gain programmatic access to physical devices on the Raspberry Pi for reading temperature and controlling a relay to turn the heater on or off.</li><li>I wanted external visibility into how the thermostat was working. I decided to push data to the cloud instead of keeping it on the device so that an external “watcher” can send an alarm if something isn’t working correctly. I wouldn’t want a thermostat not to turn on when it’s supposed to (freezing things is bad) or stay on too long (100 degrees is also bad and a potential fire hazard).</li><li>I chose a REST interface for controlling the thermostat — making it easy to connect something to it later. I might make a Flutter app for my phone (more new things to figure out!) or a web interface.</li><li>I chose to address security through physical network access and firewalls and not implement a security layer on the API. I am saving implementing security for the 2.0 version, which keeps the initial development cleaner and easier.</li><li>I would follow the main principles from Zero to Production in organizing the project and utilize many of the same external Rust packages. I liked the coding style and choices made in the book and consider it a best practices reference.</li></ol><p>I intended this to be a hobby project — not a commercial production-ready implementation. When I install this for actual use, I will have a backup system to ensure nothing bad happens. Things work in the project’s current state — but I don’t profess to have the system hardened. More testing is needed before I’ll trust this with anything important.</p><p>The following diagram depicts the system architecture for the solution:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*A2kQwKUZ9G5lsE928ukYQw.png" /></figure><p>The subsystems of the application are as follows:</p><ul><li><strong>Shared Data</strong> — This holds the current state of the application and is accessed from the other subsystems as needed. Each subsystem runs on independent threads, so we wrap the shared data to protect it when multiple threads try to read/set its data independently.</li><li><strong>Web (HTTP) Interface</strong> — We run an actix-web server, allowing for multiple HTTP clients. This interface allows external access to the thermostat — giving it the “wireless” functionality we desire. These interfaces enable setting the thermostat value (the desired minimum temperature) and retrieving the current temperature and thermostat setting.</li><li><strong>Read Current Temperature</strong> — An independent thread polls the temperature sensor periodically. Each time it reads a temperature value, it changes the internal state and then pushes those values to the external cloud storage.</li><li><strong>Set Thermostat</strong> — Another independent thread polls the Shared Data to see if the thermostat should be on or off. When the temperature is below the thermostat value, the external control relay is set on — else, it is set off. A minimum time on/off threshold avoids thrashing, so we store the control relay change time in the Shared Data, which becomes part of the logic for determining the relay state.</li><li><strong>Telemetry</strong> — All tracing and error messages are funneled through a Telemetry layer and pushed to an external collector for visibility.</li><li><strong>Cloud Data Storage</strong> — We’re using AWS as our cloud provider and utilizing a Lambda function (written in Rust!) to receive HTTP messages from the application and store them in a DynamoDB table for external processing. The cloud database is our repository of historical information on how the temperature and thermostat settings have changed over time. Eventually, a monitor against this data will alert us if something goes wrong.</li><li><strong>End-User UI</strong> — We need a way to control the thermostat and see its current state. This UI interacts with the application via HTTP to get/set data. It also can retrieve historical data from the cloud for display.</li></ul><p>The GitHub link for the source code is <a href="https://github.com/mikehentges/thermostat-pi">here</a>. I’m still fiddling with things and working on a GUI client. I plan on diving into the details of the application in a series of follow-on posts — follow me to get an update when the next in the series is published.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=45a5d35196cf" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What’s the real advantage of AI-driven technology in the Contact Center?]]></title>
            <link>https://medium.com/@mikehentges65/whats-the-real-advantage-of-ai-driven-technology-in-the-contact-center-253bf836e2bf?source=rss-8b3e41d73ef4------2</link>
            <guid isPermaLink="false">https://medium.com/p/253bf836e2bf</guid>
            <category><![CDATA[ccai]]></category>
            <category><![CDATA[ai]]></category>
            <category><![CDATA[contact-center]]></category>
            <category><![CDATA[speech-recognition]]></category>
            <dc:creator><![CDATA[Michael Hentges]]></dc:creator>
            <pubDate>Mon, 03 Oct 2022 19:09:00 GMT</pubDate>
            <atom:updated>2022-10-03T19:09:00.299Z</atom:updated>
            <content:encoded><![CDATA[<p>As I watch the contact center industry promote everything and anything AI, I’m surprised at how often AI’s benefits are drastically misrepresented. I see and hear many discussions of the benefits of AI that are arguments for automation — with no mention of specific AI benefits over other forms of automation. “Decrease costs, make customers happy” are claims that seem too good to be true and don’t provide much information on what about AI enables that to happen.</p><p>AI-based systems, when appropriately utilized, have tangible benefits, especially when compared to the older speech-recognition-based IVR systems (or, heaven forbid, touch-tone DTMF systems), they are replacing. But I feel we are doing a disservice to AI when we don’t describe how the AI part makes the system better.</p><figure><img alt="DTMF IVR" src="https://cdn-images-1.medium.com/max/147/1*F9NjDEXDTULIY_kYuWb84g.png" /></figure><p>First, let me share a little history. IVR (Interactive Voice Response) is a technology that has been around for 40 years. At first, these systems accepted touch-tone (DTMF) input, the ubiquitous “press 1 for sales, 2 for customer service….” call directing systems that replaced human operators. Then, the systems allowed data entry over the phone (“enter your account number…”). Back-end data transactions (screen-scraping mainframe terminal screens at first, then more modern API methods) would pull data to the caller for self-service (“your current balance is …”). The return on investment for these systems was straightforward: $5.00 per minute for a human agent interaction vs. $.25 per minute for an IVR interaction (typical costs in the 1990s). If you could automate a high enough percentage of calls, the IVR systems would pay for themselves in months.</p><p>Some industries, like retail banking, could see call automation rates over 90%. The cost avoidance potential made IVR systems must-have tools for call centers. They were great at cost reduction — but terrible for customer service. The complexity of end-user needs pushed against the limits of the technology — trees of menus sprouted everywhere, and the term “IVR Hell” came to be known.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*4PT9QF1FtgEBroxiBmEQzw.png" /></figure><p>The next major step forward was the advent of speech recognition technology. Instead of requiring callers to press buttons, systems could accept spoken input. Speech recognition technology had its limitations: allowing a wide variety of inputs required speech systems trained to a single voice. Dictation-type systems where you could talk instead of type could use this technology, but not in call centers. Speech recognition technology could recognize speech input from a general population only if a small number of specific words were allowed as input. As technology progressed, the number of different words the system could match against the input increased — but it always had some upper limit.</p><p>“Grammar” was the term used to define the permitted words at any speech input state. These “directed dialog” IVR systems would use these grammars when prompting for input, collect that information from the caller, and then go to the next input step. If the caller speaks one of the allowed words, the input is accepted — otherwise, the common “I’m sorry I didn’t get that” is spoken to get the caller to try again.</p><p>Allowing speech input was an improvement over DTMF-only IVR systems. System designers could develop natural speech input patterns, especially for “form filling” use cases where multiple data elements were needed to complete a transaction. Caller acceptance was higher than with DTMF-only systems. But, technology acquisition, development, and upkeep costs could be three times higher than a DTMF-only system — and the technical limitations still frustrated callers. These IVR systems still forced callers to figure out how to get through the prompts — to put their problems into words the system designer used. Well-designed systems made that more manageable — but too many systems struggled to provide a good user experience.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zqmPIACuT0-Cx4_kwZSgCw.jpeg" /></figure><p>So this brings us to where AI became involved. Two distinct industry trends converged, bringing AI-powered systems to the contact center. The first was web chat: pop-up boxes on websites connected users to live human agents. These systems were a hit with both end users and businesses, and web chat quickly overtook email as the primary channel for text-based communication in the Contact Center. Companies quickly recognized the need for automation of these interactions, the all-text communication path was straightforward and cheap to build, and the “dumb chatbot” was born. A desire to create better automated text interactions drove the adoption of AI systems with our first describable advantage of AI systems used in the contact center.</p><h4><strong>AI systems can define meaning from Human Conversations.</strong></h4><p>For our AI-driven chatbots, end users could ask their questions the same way they would if they were typing their questions to a human agent. The AI-powered system could pull the meaning out of the conversation without restricting the input to a specific form or grammar. That meaning usually takes the form of the “intent” of the interaction, plus any “entities” contained within the conversation. “What’s the current balance of my checking account” yields an intent of “current balance” and an entity of “checking account.” With that meaning pulled out of the interaction, an automated system has the information it needs to respond correctly. A trained AI system demonstrates its value by being more robust and accurate than hand-coded word-spotting or other rules-based algorithms without restrictions or “guidance” from the system on the input. The user interacts naturally, and the AI system figures it out.</p><p>The use of AI is not magic — a system will have a pre-defined set of intents it can react to (you don’t expect a banking system to be able to collect information needed to order a pizza…). But, it allows for a more natural interface for the end user and a more accurate and easily maintainable system for the business.</p><p>Now that we have AI-based technology for understanding human conversation happening over a text channel, the 2nd advantage of AI technology for contact centers is applicable.</p><h4><strong>AI-based speech-to-text (speech recognition) technology provides a reliable and efficient means to turn human speech into corresponding text. Contact Centers can then use AI-based text processing tools to determine the meaning of voice interactions.</strong></h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*es8OAhiNHnDkw_nr9BEP-g.jpeg" /><figcaption>Anthony Brown — stock.adobe.com</figcaption></figure><p>Amazon’s Alexa was the first widely used, commercially successful speech input system that utilized new AI-driven technology to break the boundaries of previous speech recognition systems. Amazon’s Echo devices used a combination of advanced microphone technology with an AI-backed recognition engine to pull actionable intents out of end-user inputs in a way that previous phone-based systems could not. Several vendors quickly made this technology available for phone-based interactions (including Amazon!), and it is now the new standard toolset for applying automation to the contact center.</p><p>Like with text, once systems can understand the meaning of a customer inquiry, automation tools provide a means to assist with completing the interaction.</p><p><strong>So, in summary, here are the key benefits of AI technology for the Contact Center</strong>:</p><ol><li><strong>AI tools programmatically (without human intervention) determine the intent or purpose of a customer’s interaction over voice or text using a natural interface that does not hinder the customer.</strong></li><li><strong>Contact Centers can use this understanding of the customer to apply automation wherever customer communication is in text or voice. Contact Centers are using this automation successfully at several touchpoints, including:<br> → Automating a request before it reaches an agent.<br> → Assisting an agent by “listening in” on the interaction and providing cues to the agent.<br> → Automating post-call notes or analyzing completed interactions for quality.</strong></li><li><strong>The AI-based systems that do this work are less expensive to build, implement, and maintain — and are more accurate than their predecessors.</strong></li></ol><p>AI excels for use cases where your business needs to figure out what your customer is trying to accomplish when they contact you without alienating them by asking 20 questions. And while it does provide a more economical way to utilize speech input for phone-based systems, it doesn’t measurably help you with simple data capture tasks such as collecting an account number. Recognizing where AI provides leverage to your use cases can help you make more informed vendor selections for tools and services.</p><p>Enabling automation is still the core of helping Contact Centers work more efficiently. Using the best tools to build that automation allows systems to perform their automated tasks in the most customer-pleasing way. And systems pleasing to the end user get used more and detract less from the customer experience. Recognize AI for what it is — a tool for building better automation. By itself, it doesn’t do anything.</p><p>Need additional help? I can provide services to assist in use case development, vendor selection, and implementation guidance. Please reach out!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=253bf836e2bf" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>