a cheat-sheet for mathematical notation in Rust ๐Ÿฆ€ code form

Related tags


math-as-rust ๐Ÿฆ€

Based on math-as-code

This is a reference to ease developers into mathematical notation by showing comparisons with Rust code.

Motivation: Academic papers can be intimidating for self-taught graphics programmers and data wranglers :)

This guide is not yet finished. If you see errors or want to contribute, please open a ticket or send a PR.


Mathematical symbols can mean different things depending on the author, context and the field of study (linear algebra, set theory, etc). This guide may not cover all uses of a symbol. In some cases, real-world references (blog posts, publications, etc) will be cited to demonstrate how a symbol might appear in the wild.

For a more complete list, refer to Wikipedia - List of Mathematical Symbols.

For simplicity, many of the code examples here operate on floating point values and are not numerically robust. For more details on why this may be a problem, see Robust Arithmetic Notes by Mikola Lysenko.


variable name conventions

There are a variety of naming conventions depending on the context and field of study, and they are not always consistent. However, in some of the literature you may find variable names to follow a pattern like so:

  • s - italic lowercase letters for scalars (e.g. a number)
  • x - bold lowercase letters for vectors (e.g. a 2D point)
  • A - bold uppercase letters for matrices (e.g. a 3D transformation)
  • ฮธ - italic lowercase Greek letters for constants and special variables (e.g. polar angle ฮธ, theta)

This will also be the format of this guide.

Rust Math Lib?

equals symbols

There are a number of symbols resembling the equals sign =. Here are a few common examples:

  • = is for equality (values are the same)
  • โ‰  is for inequality (value are not the same)
  • โ‰ˆ is for approximately equal to (ฯ€ โ‰ˆ 3.14159)
  • := is for definition (A is defined as B)

In Rust:

// equality
2 == 3

// inequality
2 != 3

// approximately equal
extern crate is_close;

// is_close! doesn't have a third argument for tolerance, so this is false
is_close!(std::f64::consts::PI, 3.14159), true)

fn is_almost_equal(x: f64, y: f64, epsilon: f64) -> bool {
    (x - y).abs() < (10f64.powf(-epsilon))

is_almost_equal(std::f64::consts::PI, 3.14159, 1e-5) // true

Read more: programmers got this idea from the [epsilon-delta definition of limit][1]

In mathematical notation, you might see the :=, =: and = symbols being used for definition.[2]

For example, the following defines x to be another name for 2kj.


In rust, we define our variables with =.

let x = 2 * k * j

Assignment in rust variables are immutable by default.

Note: Some languages have pre-processor #define statements, which are closer to a mathematical define.

Notice that fn is a form of := as well.

fn plus(x: f64, y: f64) -> f64 {
  x + y

The following, on the other hand, represents equality:


Important: the difference between = and == can be more obvious in code than it is in math literature! In rust, a = is an instruction. You're telling the machine to interact with the namespace, add something to it or change something in it. In rust, when you write == you're asking the machine "may I have a bool?". In math, the former case is either covered by := or =, while the latter case is usually =, and you might have to do some disambiguating in your reading.

In math, when I write 1 + 1 = 2 I'm making a judgment. It's not that i'm asking the world (or the chalkboard) for a bool, it's that I'm keeping track of my beliefs. This distinction is the foundation of unit tests or assertions.

// assert in takes an expression that lands in bool and a string to be printed if it turns out false.
assert!(plus(1, 1) == 2, "DANGER: PHYSICS IS BROKEN. PLEASE STAY INSIDE.");

It's important to know when a falsehood ought to crash a program vs. when you just want a boolean value. To understand this better, read this.

square root and complex numbers

A square root operation is of the form:


In programming we use a sqrt function, like so:

println!("{}", 2f64.sqrt());
// Out: 1.4142135623730951

Complex numbers are expressions of the form complex, where a is the real part and b is the imaginary part. The imaginary number i is defined as:


println!("{}", 2f64.sqrt());
// Out: 1+1i

use num::Complex;
let complex_integer = num::Complex::new(1, 1);
println!("{}", complex_integer.sqrt());
// Out: 1.0986841134678098+0.45508986056222733i

// we can represent the basic meaning of the imaginary unit like so
let cn1 = num::complex::Complex::new(-1, 0);
let cn2 = num::complex::Complex::new(0, 1);
assert!(cn1 == cn2); // Should fail

dot & cross

The dot ยท and cross ร— symbols have different uses depending on context.

They might seem obvious, but it's important to understand the subtle differences before we continue into other sections.

scalar multiplication

Both symbols can represent simple multiplication of scalars. The following are equivalent:


In programming languages we tend to use asterisk for multiplication:

let result = 5 * 4

Often, the multiplication sign is only used to avoid ambiguity (e.g. between two numbers). Here, we can omit it entirely:


If these variables represent scalars, the code would be:

let result = 3 * k * j

vector multiplication

To denote multiplication of one vector with a scalar, or element-wise multiplication of a vector with another vector, we typically do not use the dot ยท or cross ร— symbols. These have different meanings in linear algebra, discussed shortly.

Let's take our earlier example but apply it to vectors. For element-wise vector multiplication, you might see an open dot โˆ˜ to represent the Hadamard product.[2]


In other instances, the author might explicitly define a different notation, such as a circled dot โŠ™ or a filled circle โ—.[3]

Here is how it would look in code, using arrays [x, y] to represent the 2D vectors.

let s = 3
let k = vec![1, 2]
let j = vec![2, 3]

let tmp = multiply(k, j)
let result = multiply_scalar(tmp, s)
// Out: [6, 18]

Our multiply and multiply_scalar functions look like this:

fn multiply(a: Vec<i64>, b: Vec<i64>) -> Vec<i64> {
    let it = a.iter().zip(b.iter());
    it.map(|(x, y)| x * y).collect()

fn multiply_scalar(a: Vec<i64>, scalar: i64) -> Vec<i64> {
    a.iter().map(|v| v * scalar).collect()

dot product

The dot symbol ยท can be used to denote the dot product of two vectors. Sometimes this is called the scalar product since it evaluates to a scalar.


It is a very common feature of linear algebra, and with a 3D vector it might look like this:

let k = [0, 1, 0];
let j = [1, 0, 0];

let d = dot(k, j);
// Out: 0

The result 0 tells us our vectors are perpendicular. Here is a dot function for 3-component vectors:

fn dot(a, b):
  return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]

cross product

The cross symbol ร— can be used to denote the cross product of two vectors.


In code, it would look like this:

let k = vec![0, 1, 0];
let j = vec![1, 0, 0];
let result = cross(k, j);
// Out: [ 0, 0, -1 ]

Here, we get [0, 0, -1], which is perpendicular to both k and j.

Our cross function:

fn cross(a: Vec<i64>, b: Vec<i64>) -> Vec<i64> {
    let rx = a[1] * b[2] - a[2] * b[1];
    let ry = a[2] * b[0] - a[0] * b[2];
    let rz = a[0] * b[1] - a[1] * b[0];
    vec![rx, ry, rz]


The big Greek ฮฃ (Sigma) is for Summation. In other words: summing up some numbers.


Here, i=1 says to start at 1 and end at the number above the Sigma, 100. These are the lower and upper bounds, respectively. The i to the right of the "E" tells us what we are summing. In code:

Hence, the big sigma is the std::iter::Sum module.

// Out: 5050

Tip: With whole numbers, this particular pattern can be optimized to the following (and try to grok the proof. The legend of how Gauss discovered I can only describe as "typical programmer antics"):

fn sum_to_n(n: f64) -> f64 {
    (n * (n + 1.)) / 2.

Here is another example where the i, or the "what to sum," is different:


In code:

std::iter::Sum::sum((0..n).map(|k| 2 * k + 1).into_iter())
// Out: 10000

important: range in Rust has an inclusive lower bound and exclusive upper bound, meaning that ... (0..100) is equivalent to the sum of ... for k=0 to k=n.

If you're still not absolutely fluent in indexing for these applications, spend some time with Trev Tutor on youtube.

The notation can be nested, which is much like nesting a for loop. You should evaluate the right-most sigma first, unless the author has enclosed them in parentheses to alter the order. However, in the following case, since we are dealing with finite sums, the order does not matter.


In code:

  .map(|i| (4..7i32).map(|j| 3 * i * j).sum::<i32>())
// Out: 135

capital Pi

The capital Pi or "Big Pi" is very similar to Sigma, except we are using multiplication to find the product of a sequence of values.

Take the following:


fn times(x: i64, y: i64) -> i64 {
  x * y

Or using the function std::iter::Iterator::fold

(1..7).into_iter().fold(1, times);
// # Out: 720


Pipe symbols, known as bars, can mean different things depending on the context. Below are three common uses: absolute value, Euclidean norm, and determinant.

These three features all describe the length of an object.

absolute value


For a number x, |x| means the absolute value of x. In code:

let x = -5
// Out: 5

Euclidean norm


For a vector v, โ€–vโ€– is the Euclidean norm of v. It is also referred to as the "magnitude" or "length" of a vector.

Often this is represented by double-bars to avoid ambiguity with the absolute value notation, but sometimes you may see it with single bars:


Here is an example using an array [x, y, z] to represent a 3D vector.

let v = vec![0, 4, -3]
// Out: 5

The `length** function:

fn vec_length(a: Vec<i64>) -> i64 {
    let x = a[0];
    let y = a[1];
    let z = a[2];
    return sqrt(x.pow(2) + y.pow(2) + z.pow(2));

The implementation for arbitrary length'd vectors is left as an exercise for the reader.



For a matrix A, |A| means the determinant of matrix A.

Here is an example computing the determinant of a 2x2 identity matrix

let ident_2 = [1., 0., 0., 1.];
let result = nalgebra::Matrix2::from_row_slice(&ident_2);
// Out: 1

You should watch 3blue1brown, but in short if a matrix (list of list of numbers) is interpreted as hitting a coordinate system with a squisher-stretcher-rotater, the determinant of that matrix is the measure of how much the unit area/volume of the coordinate system got squished-stretched-rotated.

// the determinant of the 100 x 100 identity matrix is still one, because the identity matrix doesn't squish, stretch, or rotate at all.
nalgebra::DMatrix::<f32>::identity(100, 100)
// Out: 1.0

// 90 degree rotation.
nalgebra::Matrix2::from_row_slice(&[0., -1., 1., 0.])
// Out: 1.0

The second matrix was the 2D rotation at 90 degrees.


In geometry, the "hat" symbol above a character is used to represent a unit vector. For example, here is the unit vector of a:


In Cartesian space, a unit vector is typically length 1. That means each part of the vector will be in the range of -1.0 to 1.0. Here we normalize a 3D vector into a unit vector:

let a = vec![0., 4., -3.];
// Out: Vec[0., 0.8, -0.6]

If a vector is that which has magnitude and direction, normalization of a vector is the operation that deletes magnitude and preserves direction.

Here is the normalize function, operating on 3D vectors:

fn normalize(a: Vec<i64>) -> Vec<i64> {
    let mut b = a.to_vec();
    let mut x = a[0];
    let mut y = a[1];
    let mut z = a[2];
    let squared_length = x * x + y * y + z * z;

    if squared_length > 0 {
        let length = sqrt(squared_length);
        b[0] = x / length;
        b[1] = y / length;
        b[2] = z / length;

    return b;


In set theory, the "element of" symbol โˆˆ and โˆ‹ can be used to describe whether something is an element of a set. For example:


Here we have a set of numbers A = { 3, 9, 14 } and we are saying 3 is an "element of" that set.

The in keyword plays the role of the elementhood function, giving a bool.

let a = vec![3, 9, 14];
// Out: true

Rust also has set. You can wrap any iterable set keyword to delete repeats.

let mut a = vec![3, 3, 3, 2, 4, 3, 3, 3, 1, 2, 4, 5, 3];
let set: std::collections::HashSet<i32> = a.into_iter().collect();
let mut set = set.into_iter().collect::<Vec<i32>>();
// Out: Vec[1, 2, 3, 4, 5]

// However the example above works even better with the dedup, which has the same effect of the set
let mut a = vec![3, 3, 3, 2, 4, 3, 3, 3, 1, 2, 4, 5, 3];
a.sort(); // sort
a.dedup(); // remove the duplicated values
// Out: Vec[1, 2, 3, 4, 5]

let a: Vec<i32> = (1..20).step_by(4).collect();
// Out: false

The backwards โˆ‹ is the same, but the order changes:


You can also use the "not an element of" symbols โˆ‰ and โˆŒ like so:


Which you know is represented by the ! keyword in Rust. For instance negating a value !(1 == 2)

common number sets

You may see some some large Blackboard letters among equations. Often, these are used to describe sets.

For example, we might describe k to be an element of the set โ„.


Listed below are a few common sets and their symbols.

โ„ real numbers

The large โ„ describes the set of real numbers. These include integers, as well as rational and irrational numbers.

Computers approximate โ„ with float.

You can use is_finite to check "k โˆˆ โ„".

fn is_real(x: T) -> bool {
// Out: true

Again, you may elevate that bool to an assertion that makes-or-breaks the whole program with the assert keyword when you see fit.

โ„š rational numbers

Rational numbers are real numbers that can be expressed as a fraction, or ratio. Rational numbers cannot have zero as a denominator.

Imagine taking โ„ and removing radicals (like num::Float::sqrt) and logarithms (in a family called transcendentals), that's basically what โ„š is, at least enough for a rough first approximation.

This also means that all integers are rational numbers, since the denominator can be expressed as 1.

An irrational number, on the other hand, is one that cannot be expressed as a ratio, like ฯ€ (std::f64::consts::PI).

โ„ค integers

An integer is a whole number. Just imagine starting from zero and one and building out an inventory with addition and subtraction.

An integer has no division, no decimals.

let i:i32 = 1;
let j:i64 = 999;

โ„• natural numbers

A natural number, a non-negative integer.

This is actually the only set invented by the flying spaghetti monster: as for the others, humans have themselves to blame.

Depending on the context and field of study, the set may or may not start with zero.

...ok but, between you and me, they 200% start with zero.

โ„• also happens to be the first inductive construction in the study of datatypes, consisting of a single axiom ("Zero is a โ„•") and a single inference rule ("if n is a โ„• then n + 1 is also a โ„•")

โ„• in Rust natural numbers are u8, u16, u32, u64, u128.

โ„‚ complex numbers

As we saw earlier, the complex numbers are a particular struct.

A complex number is a combination of a real and imaginary number, viewed as a co-ordinate in the 2D plane. For more info, see A Visual, Intuitive Guide to Imaginary Numbers.

We can say โ„‚ = {a + b*i | a,b โˆˆ โ„}, which is a notation called


Functions are fundamental features of mathematics, and the concept is fairly easy to translate into code.

A function transforms an input into an output value. For example, the following is a function:


We can give this function a name. Commonly, we use ฦ’ to describe a function, but it could be named A or anything else.


In code, we might name it square and write it like this:

fn square(x: i64) -> i64 {

Sometimes a function is not named, and instead the output is written.


In the above example, x is the input, the transformation is squaring, and y is the output. We can express this as an equation because, conventionally, we think of x as input and y as output.

But we have a stronger idea called anonymous functions to generalize this.

Just as we can name strings x = "Alonzo" then call them with their names or we can just pass string literals, we also have function literals.

Math first, then Rust:

x โ†ฆ x^2 is equivalent to the equational description above.

let lambda_square = |x: i64| x.pow(2);

Functions can also have multiple parameters, like in a programming language. These are known as arguments in mathematics, and the number of arguments a function takes is known as the arity of the function.


piecewise function

Some functions will use different relationships depending on the input value, x.

The following function ฦ’ chooses between two "sub functions" depending on the input value.


This is very similar to if / else in code. The right-side conditions are often written as "for x < 0" or "if x = 0". If the condition is true, the function to the left is used.

In piecewise functions, "otherwise" and "elsewhere" are analogous to the else statement in code.

fn f(x: f64) -> f64 {
    if x >= 1. {
        x.powf(2.) - x / x
    } else {

common functions

There are some function names that are ubiquitous in mathematics. For a programmer, these might be analogous to functions.

One such example is the sgn function. This is the signum or sign function. Let's use piecewise function notation to describe it:


In code, it might look like this:

fn sgn(x: i32) -> i32 {
    match x {
        x if x < 0 => -1,
        x if x > 0 => 1,
        _ => 0,

See signum for this function as a module.

Other examples of such functions: sin, cos, tan can be found on the Rust standard library as traits on module num

function notation

In some literature, functions may be defined with more explicit notation. For example, let's go back to the square function we mentioned earlier:


It might also be written in the following form:


The arrow here with a tail typically means "maps to," as in x maps to x2.

Sometimes, when it isn't obvious, the notation will also describe the domain and codomain of the function. A more formal definition of ฦ’ might be written as:


A function's domain and codomain is a bit like its input and output types, respectively. Here's another example, using our earlier sgn function, which outputs an integer:


The arrow here (without a tail) is used to map one set to another.


The prime symbol (โ€ฒ) is often used in variable names to describe things which are similar, without giving it a different name altogether. It can describe the "next value" after some transformation.

For example, if we take a 2D point (x, y) and rotate it, you might name the result (xโ€ฒ, yโ€ฒ). Or, the transpose of matrix M might be named Mโ€ฒ.

In code, we typically just assign the variable a more descriptive name, like transformedPosition.

For a mathematical function, the prime symbol often describes the derivative of that function. Derivatives will be explained in a future section. Let's take our earlier function:


Its derivative could be written with a prime โ€ฒ symbol:


In code:

fn f(x: i64) -> i64 {

fn f_prime(x: i64) -> i64 {
    return 2 * x;

Multiple prime symbols can be used to describe the second derivative ฦ’โ€ฒโ€ฒ and third derivative ฦ’โ€ฒโ€ฒโ€ฒ. After this, authors typically express higher orders with roman numerals ฦ’IV or superscript numbers ฦ’(n).

floor & ceiling

The special brackets โŒŠxโŒ‹ and โŒˆxโŒ‰ represent the floor and ceil functions, respectively.



In code:

fn floor(x: T) -> T {

fn ceil(x: T) -> T {

When the two symbols are mixed โŒŠxโŒ‰, it typically represents a function that rounds to the nearest integer:


In code:

fn round(x: T) -> T {


Arrows are often used in function notation. Here are a few other areas you might see them.

material implication

Arrows like โ‡’ and โ†’ are sometimes used in logic for material implication. That is, if A is true, then B is also true.


Interpreting this as code might look like this:

if a == true {
  assert!(b == true);

The arrows can go in either direction โ‡ โ‡’, or both โ‡”. When A โ‡’ B and B โ‡’ A, they are said to be equivalent:



In math, the < > โ‰ค and โ‰ฅ are typically used in the same way we use them in code: less than, greater than, less than or equal to and greater than or equal to, respectively.

50 > 2 == true;
2 < 10 == true;
3 <= 4 == true;
4 >= 4 == true;

On rare occasions you might see a slash through these symbols, to describe not. As in, k is "not greater than" j.


The โ‰ช and โ‰ซ are sometimes used to represent significant inequality. That is, k is an order of magnitude larger than j.


conjunction & disjunction

Another use of arrows in logic is conjunction โˆง and disjunction โˆจ. They are analogous to a programmer's AND and OR operators, respectively.

The following shows conjunction โˆง, the logical AND.


In Rust, we use &&. Assuming k is a natural number, the logic implies that k is 3:

if k > 2 && k < 4 {
  assert!(k == 3);

Since both sides are equivalent โ‡”, it also implies the following:

if k == 3 {
  assert!(k > 2 && k < 4);

The down arrow โˆจ is logical disjunction, like the OR operator.


In code:

A || B

logical negation

Occasionally, the ยฌ, ~ and ! symbols are used to represent logical NOT. For example, ยฌA is only true if A is false.

Here is a simple example using the not symbol:


An example of how we might interpret this in code:

if (x != y) {
  assert(!(x == y))

Note: The tilde ~ has many different meanings depending on context. For example, row equivalence (matrix theory) or same order of magnitude (discussed in equality).


Sometimes a function deals with real numbers restricted to some range of values, such a constraint can be represented using an interval

For example we can represent the numbers between zero and one including/not including zero and/or one as:

  • Not including zero or one: interval-opened-left-opened-right
  • Including zero or but not one: interval-closed-left-opened-right
  • Not including zero but including one: interval-opened-left-closed-right
  • Including zero and one: interval-closed-left-closed-right

For example we to indicate that a point x is in the unit cube in 3D we say:


In code we can represent an interval using a two element 1d array:

Like this guide? Suggest some more features or send us a Pull Request!


This repo is havely inspired, based on the original project by math-as-code, thank you very much to Jam3 for creating that awesome guide which serves as huge inspiration for me to build a Rust version ๐Ÿฆ€


For details on how to contribute, see CONTRIBUTING.md.


MIT, see LICENSE.md for details

Eduardo Pereira
Software Engineer, Gamedev, Blockchain Enthusiast, and Guitar Student
Eduardo Pereira
A bunch of links to blog posts, articles, videos, etc for learning Rust

rust-learning A bunch of links to blog posts, articles, videos, etc for learning Rust. Feel free to submit a pull request if you have some links/resou

Camille TJHOA 6.7k Jun 14, 2021
Rust explained using easy English

Update 22 December 2020: mdBook can be found here. 28 November 2020: Now also available in simplified Chinese thanks to kumakichi! 1 February 2021: No

null 6k Jun 14, 2021
Leetcode Solutions in Rust, Advent of Code Solutions in Rust and more

RUST GYM Rust Solutions Leetcode Solutions in Rust AdventOfCode Solutions in Rust This project demostrates how to create Data Structures and to implem

Larry Fantasy 158 Jun 14, 2021
The missing batteries of Rust

stdx - The missing batteries of Rust New to Rust and don't yet know what crates to use? stdx has the best crates. Current revision: stdx 0.119.0-rc, f

Brian Anderson 1.4k Jun 11, 2021
Business Process eXecution Engine

BPXE (Business Process eXecution Engine) BPMN 2.0 based business process execution engine implemented in Rust. BPMN stands for Business Process Model

BPXE 14 May 31, 2021
A peer-reviewed collection of articles/talks/repos which teach concise, idiomatic Rust.

This repository collects resources for writing clean, idiomatic Rust code. Please bring your own. ?? Idiomatic coding means following the conventions

Matthias 2.6k Jun 14, 2021
Portals in form of a Mobius strip.

Portals in form of a Mobius strip Online demo: https://optozorax.github.io/mobius_portal/ If you have a low FPS, you can reduce window size of your br

ilya sheprut 13 Mar 22, 2021
Experimental Quantum Computer Simulator + Quantum Chess Implementation

Quantum Chess A somewhat hacky implementation of this paper (made in a week over a holiday). It's not heavily tested and probably has some bugs still

null 18 Feb 10, 2021
Blog posts, mostly about Rust.

Sean Chen's Blog ?? Blog posts, mostly about Rust. Posts Date Title 2021-04-06 A Beginner's Guide to Handling Errors in Rust 2021-01-23 Implementing a

Sean Chen 11 May 23, 2021
๐Ÿ‹: A General Lock following paper "Optimistic Lock Coupling: A Scalable and Efficient General-Purpose Synchronization Method"

Optimistic Lock Coupling from paper "Optimistic Lock Coupling: A Scalable and Efficient General-Purpose Synchronization Method" In actual projects, th

LemonHX 16 Jun 5, 2021
List of Rust books

Rust Books Books Starter Books Advanced Books Resources Books Starter Books The Rust Programming Language Free Welcome! This book will teach you about

Spiros Gerokostas 1.3k Jun 15, 2021
:crab: Small exercises to get you used to reading and writing Rust code!

rustlings ?? โค๏ธ Greetings and welcome to rustlings. This project contains small exercises to get you used to reading and writing Rust code. This inclu

The Rust Programming Language 16.9k Jun 13, 2021
A programming language somewhat resembling cellular processes.

cytosol An embeddable programming language somewhat resembling cellular processes. State of the implementation tokenising parsing semantic analysis an

null 31 Apr 2, 2021
An inquiry into nondogmatic software development. An experiment showing double performance of the code running on JVM comparing to equivalent native C code.

java-2-times-faster-than-c An experiment showing double performance of the code running on JVM comparing to equivalent native C code โš ๏ธ The title of t

xemantic 47 May 16, 2021