Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streamline introduction.md of luscious-lasagna #1691

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 32 additions & 79 deletions exercises/concept/lucians-luscious-lasagna/.docs/introduction.md
Original file line number Diff line number Diff line change
@@ -1,104 +1,57 @@
# Introduction

In Rust, assigning a value to a name is referred to as a _binding_. Bindings are immutable unless declared with the `mut` keyword. As Rust is a statically-typed language, each binding has a type known at compile-time.
## Functions

Bindings are most commonly defined using the `let` keyword. Specifying a binding's type is optional for most bindings, as Rust's _type inference_ can usually infer the type based on their value. A binding looks like this:

```rust
// Automatically inferred type
let fingers = 10;
```

Functions are _items_. Where bindings typically refer to a particular value, items refer to a unit of code organization, typically a function or a module, which is available throughout the lifetime of the program. A function automatically returns the result of its last expression. A function may have 0 or more parameters, which are bindings with a lifetime of the function call.

Type inference is theoretically possible for functions, but is disabled as an intentional language design choice. While this means that you need to spend a little more time when writing code to specify precisely what a function's input and output types are, you save the time when you're reading the code, because all the input and output types are explicitly defined.
Functions are a fundamental building block of Rust programs.
Let's look at a function to get an idea of what's going on:

```rust
fn add(x: i32, y: i32) -> i32 {
x + y
}
```

Invoking a function is done by specifying its name followed by parentheses. If the function requires parameters, an argument must be specified for each within the parentheses.

```rust
let five = add(2, 3);
```

If a binding's type cannot be inferred, the compiler will report an error. To fix this, add an explicit type annotation to the binding.
The first keyword is `pub`, a visibility modifier.
It is optional and you will learn about it later, no need to worry about it now.

```rust
// Explicit type annotation
let fingers: i32 = 10;
```
The keyword `fn` and the name of the function are mandatory.

Items in Rust can be used before or after they are defined, because they have a static lifetime. Bindings, on the other hand, can only be used _after_ they have been defined. Using a binding before it has been defined results in a compile error.
A function may have zero or more parameters enclosed in parentheses `()`.
A parameter consist of its name and type, separated by a colon.
Multiple parameters are separated by commas.

The return type is specified after an arrow `->`.
It is omitted if the function doesn't return anything.
This is common when the function's purpose is to perform a side effect.
```rust
fn main() {
// `fn add` hasn't yet been defined, but that's perfectly ok
dbg!(add(3, 4));
}

fn add(x: i32, y: i32) -> i32 {
x + y
fn perform_side_effect() {
// body
}
```

```rust
// this won't compile; `a` is used before its binding is defined
let b = a;
let a = x + y;
```

Rust uses curly braces (`{}`) to define a scope. A binding defined within a scope can't escape from it.

```rust
let a = 1;
dbg!(a); // 1
{
// Here, we re-bind `a` to a new value, which is still immutable.
// This technique is called _shadowing_. The new binding is constrained to
// this anonymous scope. Outside this scope, the previous binding still
// applies.
let a = 2;
let b = 3;
dbg!(a, b); // 2, 3
}
// can't use `b` anymore because it is out of scope
// dbg!(b);

// The shadowed `a` in the inner scope above has fallen out of scope,
// leaving us with our original binding.
dbg!(a); // 1
```
Lastly, the body of the function is enclosed in curly braces `{}`.
A function automatically returns the result of its last expression.
Simple functions like the one above can therefore consist of just an expression.

Rust items are often organized in modules. Each crate is implicitly a module, but it can define inner sub-modules of arbitrary depth. A module groups related functionality and is defined using the `mod` keyword.
Invoking a function is done by specifying its name followed by parentheses.
If the function requires arguments, they must be specified within the parentheses.

```rust
mod calc_i32 {
fn add(a: i32, b: i32) -> i32 { a + b }
fn sub(a: i32, b: i32) -> i32 { a - b }
fn mul(a: i32, b: i32) -> i32 { a * b }
fn div(a: i32, b: i32) -> i32 { a / b }
}
add(2, 3) // evaluates to 5
```

Rust supports two types of comments. The keyword `//` indicates a single-line comment; everything following the keyword until the end of the line is ignored. The keywords `/*` and `*/` indicate a multi-line comment; everything within those two keywords is ignored. It is idiomatic and good practice to prefer single-line comments.
## Integers and arithmetic

Rust also supports doc-comments, which show up in the generated documentation produced by `cargo doc`. Outer doc comments are formed with the keyword `///`, which acts identically to the `//` keyword. They apply to the item which follows them, such as a function:
The parameters and the return value of the above function all have the same type: `i32`.
This is a 32-bit, signed integer.
You will learn more about what that means exactly later.
For now it's enough to know that an `i32` can store positive and negative *whole* numbers.

```rust
/// The `add` function produces the sum of its arguments.
fn add(x: i32, y: i32) -> i32 { x + y }
```

Inner doc comments are formed with the keyword `//!`, which acts identically to the `//` keyword. They apply to the item enclosing them, such as a module:

```rust
mod my_cool_module {
//! This module is the bee's knees.
}
```
Rust provides several arithmetic operators to perform calculations.
The most common ones are:
- `+` for addition
- `-` for subtraction
- `*` for multiplication
- `/` for division (note that this rounds towards zero)

Doc comments can be of arbitrary length and contain markdown, which is rendered into the generated documentation.
There are more, and you will learn about them later.