The Tao of programming

The goal of this blog is to help you learn Rust, while teaching you to think like a professional programmer. This combines the best features of mathematics, engineering, and natural science.

I’ll use a formal language like mathematicians do. That language is Rust, and I’ll use it to express my ideas.

We will design things, assemble components into systems and test tradeoffs among alternatives. Like engineers.

We will observe the behaviour of complex systems, form hypotheses, and test predictions. Like scientists do.

The most important skill for a computer scientist is problem solving.

Problem solving means the ability to do three things:

  • find problems

  • think about solutions,

  • and express a solution in a clear and accurate way.

Learning to program is an excellent opportunity to practice problem-solving skills. That’s why I call this post “The Tao of programming”.

On one level, you will be learning to program, a useful skill by itself. On another level, you will use programming as a means to an end. As we go along, that end will become clearer.

What is a program?

A program is a sequence of instructions that specifies how to perform a computation.

The computation might be something mathematical. Like solving a system of equations or finding the roots of a polynomial.

It can be searching and replacing text in a document, or processing an image or playing a video.

The details look different in different languages. A few basic instructions appear in every language:

input: Get data from the keyboard, a file, the network, or another device.

output: Display data on the screen, save it in a file, or send it over the network.

math: Perform basic mathematical operations like addition and multiplication.

conditional execution: Check for certain conditions and run the appropriate code.

repetition: Perform an action over and over again, with or without variation.

Believe it or not, that’s pretty much all there is to it. Instructions that look like these make up every program you’ve ever used . You can think of programming as breaking a large, complex task into smaller subtasks. You repeat this until the subtasks are simple enough to do with one of these basic instructions.

Running Rust

To install Rust and related software on your computer can be a challenge. If you are familiar with your operating system, you will have no trouble installing Rust. This is especially true if you are comfortable with the command-line interface. But it can be painful to learn about system administration and programming at the same time.

To avoid that problem, I recommend that you start out running Rust in a browser. Later, when you are comfortable with Rust, I’ll help you figure out how to install Rust on your computer.

If you’re impatient, you can go to Rusts Installation Page or Other Rust Installation Methods. I recommend using the rustup tool from the Rust Installation Page to install. It will also install a few other tools that will come in handy later on.

There are a few web pages you can use to run Rust. If you already have a favorite, go ahead and use it. Otherwise I recommend Rust Playground.

Now you’re ready to get started. From here on, I assume that you know how to access the Rust Playground and run your code.

The first program

The first program you write in a new language will usually be “Hello, World!”. In Rust, it looks like this:

fn main() {
   println!("Hello, world!");
}

This is an example of a print statement, although it doesn’t actually print anything on paper. It displays a result on the screen. In this case, the result is the words

Hello world!

The quotation marks in the program show the beginning and end of the text the program should display. They don’t appear in the result.

The ! and parentheses show that println! is a macro. We’ll be writing our own macros later.

This distinction will make more sense soon, but that’s enough to get started.

Arithmetic operators

After “Hello, World”, the next step is arithmetic. Rust provides operators, special symbols that represent computations like addition and multiplication.

The operators +, -, and * perform addition, subtraction, and multiplication:

fn main() {
   let a = 40 + 2;
   let b = 43 - 1;
   let c = 6 * 7;
   println!("a={}, b={}, c={}", a, b, c);
}

The output from this is: a=42, b=42, c=42

The contents of the parentheses in println! look a little weird, don’t they?

We use {} to tell the compiler to replace the {} with a variable. It picks the variable to replace the braces with in the order in which it comes after the last quote. The contents of the first variable replaces the first braces. The contents of the second variable replaces the second braces, and so on.

A variable is a named reference to a value, and we’ll talk more about variables in the next post. For now, think of variables as labels that you attach to a value. In the program above the variables have the names a,b and c, and they all contain the same value, 42.

The operator / performs division:

fn main() {
   let a = 84/2;
   println!("a={}",a);
}

The output from this is: a=42

Values and types

A value is one of the basic things a program works with, like a letter or a number. Some values we have seen so far are 40, 42, 43, and ”Hello world!”.

These values belong to different types. Rust has a whole range of different types. Numbers are either integers, or floating-point numbers of varying size and precision. Text strings usually belong to the type String.

All these different types exist to help Rust acheive maximal safety and speed. One of the most common mistakes when it comes to security in software is faulty use of memory. Rust forces you to be more careful when you think about memory than other languages.

Memory is also important for performance. If you want fast execution you should use variables that take up as little memory as possible.

When you type a large integer, you might want to use commas between groups of digits, as in 1,000,000. This is not a legal integer in Rust, and it will make the compiler very unhappy:

fn main() {
	let a = 1,000,005;
   println!("a={}", a);
}

Output:

   error: expected one of ., ;`, `?`, or an operator, found `,`

     --> src/main.rs:2:11

     |
   2 |     let a = 1,000,005;
     |              ^ expected one of `.`, `;`, `?`, or an operator

	error: aborting due to previous error

	error: could not compile `playground`.

	To learn more, run the command again with --verbose.

That’s not what we expected at all! But at least we got to see that the error messages are pretty clear and helpful. It tells us exactly where it failed, and why, making it easy to fix. Let’s take a little detour and see why the compiler is so finicky.

Before we do, let me explain that you may use the _ (underscore) as a separator in large numbers. Like so: let a = 1_000_005;, this is OK, and may help in making large numbers more readable.

Now let’s take that detour!

Formal and natural languages

Natural languages are the languages people speak, such as English, Spanish, and French. They were not designed by people. People try to impose some order on them, but they evolved naturally.

Formal languages are languages that are designed by people for specific applications. The notation that mathematicians use is a formal language. It is perfect for expressing relationships among numbers and symbols. Chemists use a formal language to represent the chemical structure of molecules. And most importantly:

Programming languages are formal languages that people design to express computations.

Formal languages have strict syntax rules that govern the structure of statements. For example, in mathematics the statement 3 + 3 = 6 has correct syntax, but 3 + = 3$6 does not. In chemistry H2O is a correct formula, but 2Zz is not.

Syntax rules come in two flavors, one is about tokens and the other about structure.

Tokens are the basic elements of the language. They can be words, numbers, and chemical elements. One of the problems with 3 + = 3$6 is that \$ is not a legal token in mathematics (at least as far as I know). Likewise, 2Zz is not legal because there is no element with the abbreviation Zz.

The second type of syntax rule is about the way tokens combine. The equation 3 + /3 is illegal because even though + and / are legal tokens, you can’t have one right after the other. Likewise, in a chemical formula the subscript comes after the element name, not before.

This is @ well-structured Engli\$h sentence with invalid t*kens in it. This sentence all valid tokens has, but invalid structure with.

When you read a sentence in English, or a statement in a formal language, you have to figure out the structure. This process is called parsing .

Formal and natural languages have many features in common. Like tokens, structure, and syntax. But there are differences:

**ambiguity:**
Natural languages are full of ambiguity. People deal with it by using contextual clues and other information. Formal languages are designed to be nearly or completely unambiguous. This means that any statement has exactly one meaning, regardless of context.
**redundancy:**
To make up for ambiguity and reduce misunderstandings, natural languages use a lot of redundancy. As a result, they are often verbose. Formal languages are less redundant and more concise.
**literalness:**
Natural languages are full of idiom and metaphor. If I say, “The penny dropped”, there is probably no penny and nothing dropping. Formal languages mean exactly what they say.

Because we all grow up speaking natural languages, it can be hard to adjust to formal languages. The difference between formal and natural language is a lot like the difference between poetry and prose.

**Poetry:**
Words are used for their sounds as well as for their meaning, and the whole poem together creates an effect or emotional response. Ambiguity is not only common but often deliberate.
**Prose:**
The literal meaning of words is more important, and the structure contributes more meaning. Prose is more amenable to analysis than poetry but still often ambiguous.
**Programs:**
The meaning of a computer program is unambiguous and literal. It can be understood entirely by analysis of the tokens and structure.

Formal languages are more dense than natural languages, so it takes longer time to read them. Also, the structure is important, so it is not always best to read from top to bottom, left to right. Instead, learn to parse the program in your head. Identifying the tokens and interpreting the structure. Finally, the details matter. You can get away with small errors in spelling and punctuation in natural languages. But they can make a big difference in a formal language.

Debugging

Programmers make mistakes. For whimsical reasons, programming errors are called bugs. So, the process of tracking them down is called debugging.

Programming, and especially debugging, sometimes brings out strong emotions. If you are struggling with a difficult bug, you might feel angry, despondent, or embarrassed. There is evidence that people naturally respond to computers as if they were people. When computers work well, we think of them as teammates. When they seem obstinate or rude, we respond to them the same way we respond to rude, obstinate people. (Reeves and Nass, The Media Equation: How People Treat Computers, Television, and New Media Like Real People and Places ).

Preparing for these reactions might help you deal with them. One approach is to think of the computer as an employee. He has certain strengths, like speed and precision. He also has weaknesses, like lack of empathy and inability to grasp the big picture.

Your job is to be a good manager: find ways to take advantage of the strengths and mitigate the weaknesses. Find ways to use your emotions to engage with the problem. But don’t let your emotional reactions interfere with your ability to work effectively.

About the author

For the last three decades, I've worked with a variety of technologies. I'm currently focused on fullstack development. On my day to day job, I'm a senior teacher and course developer at a higher vocational school in Malmoe, Sweden. I'm also an occasional tech speaker and a mentor. Do you want to know more? Visit my website!