How to compute an arithmetic operation between integer numbers or between floating-point numbers
How to write a program containing several statements
How to print strings in several lines
Adding Integer Numbers
Let’s see how to compute the sum of two integer numbers, for example, 80 and 34.
The execution will print: The sum is 114.
The second argument of the print macro is the expression 80 + 34.
The compiler does not store into the executable file such numbers as they appear in source code, using the decimal format. The compiler may convert the two numbers into binary format, and then it may store into the executable file such binary numbers and the addition machine language instruction. At runtime, the two binary numbers are added, obtaining the number 114 in binary format.
But, as this expression contains only constant values, the compiler probably optimizes the addition operation by directly evaluating that expression, obtaining the integer number 114; then it stores that number, in binary format, into the executable file.
In both cases, at runtime, the resulting binary number is converted to the three-character string “114” in decimal format, and then the placeholder {} of the literal string is replaced by the generated string. Finally, of course, the resulting string is printed to the console.
Notice that the string The sum is 114 has been generated by the program; it is not present in source code, so it is still a string, but not a literal string.
Similarly, the two-character sequence 80 represents an integer number directly in the source code, and so it is called a literal integer. The same holds for the two characters 34. Instead, the integer number 114 is not a literal integer, as it does not appear in source code.
whose execution will print 34 + 80 = 114.
In such a case, the second argument of the macro will be put where there is the first placeholder, the third argument where there is the second placeholder, and the fourth argument where there is the third placeholder.
You can specify hundreds of arguments for the print macro, as long as the arguments after the first one are as many as the placeholders {} inside the first argument.
Other Operations Between Integer Numbers
This will print 87.
Let’s see why.
Such a formula is evaluated by the Rust compiler exactly as the C compiler would.
First, the operations in parentheses, 23 - 6 and 3 + 4, are evaluated, obtaining, respectively, 17 and 7.
At this point, our expression has become 17 % 5 + 20 * 30 / 7.
Then, the multiplication and division operations are evaluated, as they have precedence over addition and subtraction. Operations of the same precedence are evaluated in order from left to right.
17 % 5 is the remainder of the integer division operation. The remainder of the operation 17 / 5 is 2. The expression 20 * 30 is evaluated before the following division, as it is at its left. Its result is 600.
At this point, our expression has become 2 + 600 / 7.
Then, the integer division with truncation 600 / 7 is performed, and so our expression becomes 2 + 85.
Finally, the sum is evaluated, the result is formatted in decimal notation, the placeholder is replaced by the formatted number, and the resulting string is printed.
These arithmetic operations are always performed on integer binary numbers, obtaining integer binary numbers, and the result is converted to decimal format only when it is used to replace the placeholder of the print macro.
The Rust compiler is highly optimizing, so it tries to evaluate directly at compile time the expressions that it is possible to evaluate using the information available in source code. Our expression is made only of literal integers, and so the whole expression will be evaluated already at compile time, storing into the executable program only the result to print. Though, conceptually, usually you can think that all the computations are performed at runtime.
Floating-Point Arithmetic
Let’s see how to compute the sum between two numbers with fractional parts. For example, let’s add 80.3 and 34.9.
It will print The sum is 115.1.
It will print The sum is 115.19999999999999.
This will surprise those who would expect 115.2 as the result.
This phenomenon also happens in many other programming languages; it is due to the fact that Rust, like most programming languages, performs computations involving noninteger numbers using the floating-point format. But here we won’t treat this format any more.
It will print 87.71428571428571.
Let’s see why.
By putting a dot after a literal number, it is transformed into a literal floating-point number. Some programming languages require a digit after the dot, but not Rust. The precedence rules are the same as those for integer arithmetic. Division has a different result, though.
Let’s see how the evaluation of the expression is performed.
The evaluation of 23. - 6. and of 3. + 4. is similar to that of integer numbers.
By evaluating 17. % 5., 2. is obtained, similarly to integer numbers. Such an operator does not exist in C language for floating-point numbers. It corresponds to the expression fmod(17., 5.) of the C standard library.
By evaluating 20. * 30., 600. is obtained, similarly to integer numbers.
The expression 600. / 7. performs a floating-point number division. The theoretical result of such division is a number that cannot be exactly represented either in binary notation or in decimal notation. Internally, a binary-format approximate representation is generated; if you ask Rust to convert such a binary number into a decimal format, you would get the approximate representation 85.71428571428571.
Finally, the value 2. is added to such a binary number, obtaining another value that cannot be exactly represented, which is printed in the way shown earlier.
However, this one is a syntax-only limitation, not an operative one; anyway, machine code cannot sum an integer number and a floating-point number without first converting one of the two operands to the type of the other operand. When a C compiler encounters the expression 2.7 + 1, it implicitly emits the machine language instruction to convert the integer number 1 to a floating-point number; or better, being that 1 is a constant, it is converted to a floating-point number at compile time. In Rust, such conversions must be explicit.
At last, a note about the “%” operator. This is often improperly named modulo operator. Well, it should be better named remainder operator , because the mathematical modulo operator has a different behavior for negative numbers.
This will print -2 -0.19999999999999996.
Sequences of Statements
This will print 80 + 34 = 114.
The program now contains three statements, each of them terminated by the “;” character. Such statements are executed in order of appearance.
its result wouldn’t change. Actually, additional white spaces (blanks, tabs, and line breaks) are ignored.
Indent lines by four spaces inside functions.
Avoid adding several consecutive spaces inside statements.
Avoid exceeding 80 columns, possibly splitting long statements into several lines.
Breaking Literal Strings
As you can see, the literal string contains all the characters that in the source file are between the start and the end of the string, including newline characters and line leading spaces.
By adding a backslash character (“”) inside a literal string, just before the end of a line, the resulting string will contain neither that end-of-line character nor the following spaces; therefore, the leading spaces of the next line are omitted. Given that we wanted at least one space before each break, we inserted such spaces just before the backslashes.
The first solution has the drawback of being disrespectful of indentation conventions, and therefore usually the second solution is preferable. In such a solution, at the end of the lines there is the sequence , which is codified as a newline sequence, and then another backslash to exclude from the string the source code newline and the following spaces.