## Values and types

Mun is a statically typed language, which helps to detect type-related errors at compile-time. A type error is an invalid operation on a given type, such as an integer divided by a string, trying to access a field that doesn't exist, or calling a function with the wrong number of arguments.

Some languages require a programmer to explicitly annotate syntactic constructs with type information:

```
int foo = 3 + 4;
```

However, often variable types can be inferred by their usage. Mun uses type inferencing to determine variable types at compile time. However, you are still forced to explicitly annotate variables in a few locations to ensure a contract between interdependent code.

```
# pub fn main() {
# bar(1);
# }
fn bar(a: i32) -> i32 {
let foo = 3 + a;
foo
}
```

Here, the parameter `a`

and the return type must be annotated because it solidifies the signature of the function.
The type of `foo`

can be inferred through its usage.

### Integer types

An integer is a number without a fractional component.
Table 3-1 shows the built-in integer types in Mun.
Each variant can be either signed or unsigned and has an explicit size.
*Signed* and *unsigned* refer to whether it is necessary to have a sign that indicates the possibility for the number to be negative or positive.

Length | Signed | Unsigned |
---|---|---|

8-bit | `i8` | `u8` |

16-bit | `i16` | `u16` |

32-bit | `i32` | `u32` |

64-bit | `i64` | `u64` |

128-bit | `i128` | `u128` |

arch | `isize` | `usize` |

Signed integer types start with `i`

, unsigned integer types with `u`

, followed by the number of bits that the integer value takes up.
Each signed variant can store numbers from -(2^{n - 1}) to 2^{n - 1} - 1 inclusive, where *n* is the number of bits that variant uses.
Unsigned variants can store numbers from 0 to 2^{n - 1}.
By default Mun uses 32-bit signed integers.

The size of the `isize`

and `usize`

types depend on the target architecture.
On 64-bit architectures,`isize`

and `usize`

types are 64 bits large, whereas on 32-bit architectures they are 32 bits in size.

### Floating-Point Types

Real (or *floating-point*) numbers (i.e. numbers with a fractional component) are represented according to the IEEE-754 standard.
The `f32`

type is a single-precision float of 32 bits, and the `f64`

type has double precision - requiring 64 bits.

```
pub fn main() {
let f = 3.0; // f64
}
```

### The Boolean Type

The `bool`

(or *boolean*) type has two values, `true`

and `false`

, that are used to evaluate conditions.
It takes up one 1 byte (or 8 bits).

```
pub fn main() {
let t = true;
let f: bool = false; // with explicit type annotation
}
```

### Literals

There are three types of literals in Mun: integer, floating-point and boolean literals.

A boolean literal is either `true`

or `false`

.

An integer literal is a number without a decimal separator (`.`

).
It can be written as a decimal, hexadecimal, octal or binary value.
These are all examples of valid literals:

```
# pub fn main() {
let a = 367;
let b = 0xbeaf;
let c = 0o76532;
let d = 0b0101011;
# }
```

A floating-point literal comes in two forms:

- A decimal number followed by a dot which is optionally followed by another decimal literal and an
*optional*exponent. - A decimal number followed by an exponent.

Examples of valid floating-point literals are:

```
# pub fn main() {
let a: f64 = 3.1415;
let b: f64 = 3.;
let c: f64 = 314.1592654e-2;
# }
```

#### Separators

Both integer and floating-point literals can contain underscores (`_`

) to visually separate numbers from one another.
They do not have any semantic significance but can be useful to the eye.

```
# pub fn main() {
let a: i64 = 1_000_000;
let b: f64 = 1_000.12;
# }
```

#### Type suffix

Integer and floating-point literals may be followed by a type suffix to explicitly specify the type of the literal.

Literal type | Suffixes |
---|---|

Integer | `u8` , `i8` , `u16` , `i16` , `u32` , `i32` , `u64` , `i64` , `i128` , `u128` , `usize` , `isize` , `f32` , `f64` |

Floating-point | `f32` , `f64` |

Note that integer literals can have floating-point suffixes. This is not the case the other way around.

```
# pub fn main() {
let a: u8 = 128_u8;
let b: i128 = 99999999999999999_i128;
let c: f32 = 10_f32; // integer literal with float suffix
# }
```

When providing a literal, the compiler will always check if a literal value will fit the type. If not, an error will be emitted:

```
# pub fn main() {
let a: u8 = 1123123124124_u8; // literal out of range for `u8`
# }
```

### Numeric operations

Mun supports all basic mathematical operations for number types: addition, subtraction, division, multiplication, and remainder.

```
pub fn main() {
// addition
let a = 10 + 5;
// subtraction
let b = 10 - 4;
// multiplication
let c = 5 * 10;
// division
let d = 25 / 5;
// remainder
let e = 21 % 5;
}
```

Each expression in these statements uses a mathematical operator and evaluates to a single value. This is valid as long as both sides of the operator have the same type.

Unary operators are also supported:

```
pub fn main() {
let a = 4;
// negate
let b = -a;
let c = true;
// not
let d = !c;
}
```

### Shadowing

Redeclaring a variable by the same name with a `let`

statement is valid and will shadow any previous declaration in the same block.
This is often useful if you want to change the type of a variable.

```
# pub fn main() {
let a: i32 = 3;
let a: f64 = 5.0;
# }
```

### Use before initialization

All variables in Mun must be initialized before usage. Uninitialized variables can be declared but they must be assigned a value before they can be read.

```
# pub fn main() {
# let some_conditional = false;
let a: i32;
if some_conditional {
a = 4;
}
let b = a; // invalid: a is potentially uninitialized
# }
```

Note that declaring a variable without a value is often a bad code smell since the above could have better been written by *returning* a value from the `if`

/`else`

block instead of assigning to `a`

.
This avoids the use of an uninitialized value.

```
# pub fn main() {
# let some_conditional = true;
let a: i32 = if some_conditional {
4
} else {
5
}
let b = a;
# }
```