From C# to Rust: Code Basics
Tags: csharp, dotnet, rust
Published: 2020-07-28 15:13:01

---
title: From C# to Rust: Code Basics
description: "Understanding Rust's code-basics could possibly be fast-tracked by reating these concepts to the equivalent in C# and .NET."
date: 2020-07-28 13:47:00 UTC
published: true
tags: csharp, dotnet, rust
canonical_url: https://sebnilsson.com/blog/from-csharp-to-rust-code-basics/
cover_image: https://files.sebnilsson.com/web/images/rust/rust-cover-img-terminal.jpg
series: "From C# to Rust"
---

Now that we've gotten the [fundamentals](https://dev.to/sebnilsson/from-c-to-rust-fundamentals-4cgm) out of the way, we can finally get into the basics of Rust-code.

[This series](https://dev.to/sebnilsson/from-c-to-rust-introduction-4650) is written from the perspective of getting to know Rust from knowing C#. There are already many good _getting-started-guides_ out there for Rust, but **this series tries to leverage your existing .NET/C#-knowledge** to fast-track you into understanding Rust, by relating the concepts to what you already know.

_**Disclaimer** : This series is being written while I'm learning Rust, so I'm by no means an expert and maybe this might be reflected in some of the example code. If you find any blatant errors, feel free to add some feedback in the comments._

## Setup

In the post about [Rust fundamentals](https://dev.to/sebnilsson/from-c-to-rust-fundamentals-4cgm), we could read about how **Rust provides a complete toolchain** for development, which is made available by installing [Rustup, the recommended way of installing Rust](https://www.rust-lang.org/tools/install).

Create a new project in a command-line by running: `cargo new example_project`

The file **`src/main.rs` is your application's entry point** , which will be run when you execute `cargo run`. So this is a good file to start with, to **follow along in your code-editor**. This file will only contain a function called `main`, with a simple _Hello World_, when generated by Cargo:

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

```

## Code Basics

Let's start looking at some basic example-code, to go through some basic concepts in Rust. Since Rust is a C-style language, **you will probably recognize much of the syntax** you see in this code-snippet, as a C#-developer.

You can always find the full code for this series in the [`from-csharp-to-rust`-project on GitHub](https://github.com/sebnilsson/from-csharp-to-rust).

## Variables

[Variables in Rust](https://doc.rust-lang.org/reference/variables.html) are created by using the `let`-keyword, in a very similar way to the use of the `var`-keyword in C#.

```
let title = "Ghost Buster";
let year: u16 = 1984;

```

[Types are inferred](https://doc.rust-lang.org/stable/rust-by-example/types/inference.html) in Rust, as you can see with the `title`-variable. You can also specify the type, as we do with the `year`-variable, where we want to use a smaller number-type than what is inferred by default.

One important difference between Rust and C# is that **variables are immutable by default in Rust**. If you want a variable to be mutable, you have to add `mut` to the variable declaration:

```
let mut rating_avg = 7.8;

// Someone rated the movie
rating_avg = 7.9;

```

Variables can be created as [constants in Rust](https://doc.rust-lang.org/std/keyword.const.html), by using the `const`-keyword, just like in C#. These variables **have to have their types specified** :

```
const GOOD_MOVIE_LIMIT: f32 = 7.5;

```

### Naming Conventions

As you may have noticed, [Rust naming conventions](https://doc.rust-lang.org/1.0.0/style/style/naming/README.html) use [_Snake case_](https://en.wikipedia.org/wiki/Snake_case) (`example_variable_name`) for its variables. Unlike C# which usually uses [Camel case](https://en.wikipedia.org/wiki/Camel_case) (`exampleVariableName`) or Pascal case (`ExampleVariableName`) for most of its variables, fields, and properties.

The Rust-compiler will even warn you about using the wrong conventions for different namings.

_At the moment of writing, Rust-developers also seems to prefer shorter names for variables and functions, than we usually see in C# and .NET._

## Strings

In C#, we only have one string-type for all types of strings. **In Rust, there are two different string-types** you learn about at first. The primitive [`str`-type](https://doc.rust-lang.org/std/str/), which is immutable and fixed length, as well as the [`String`-type](https://doc.rust-lang.org/std/string/struct.String.html), which can be modified and grow in size.

```
let mut value = String::default();
for x in 65..68 {
    value.push(x as u8 as char);
}
// value: "ABC"

```

On Stack Overflow, a question called [_What are the differences between Rust's `String` and `str`?_](https://stackoverflow.com/questions/24158114/what-are-the-differences-between-rusts-string-and-str/24159933#24159933) the most popular answer, by far, summarizes:

> Use `String` if you need owned string data (like passing strings to other threads, or building them at runtime), and use `str` if you only need a view of a string.

Conversion between the two types is quite straight forward:

```
let title = "Ghostbusters";
// From str to String
let title_string = String::from(title);
// From String to str
let title_str = title_string.as_str();
```

In practical use, you can see Rust's `String`-type as the equivalent of `StringBuilder` in C#, while Rust's `str` is the equivalent of `ReadOnlySpan<char>`.

## Collections

In Rust, the two most common types used to create collections are [`array`](https://doc.rust-lang.org/std/primitive.array.html) and [`Vec` (vector)](https://doc.rust-lang.org/std/vec/struct.Vec.html).

The `array`-type is a fixed-size collection and is compared to an array of a type in C# and .NET, like `string[]`, `int[]`, `char[]`, and so on. The `Vec`-type can change in size and can be compared to `List<T>` in C# and .NET.

**Both types can be mutated** if the variable has the `mut`-keyword. They can also easily be converted between each other.

```
let mut arr = [1, 2, 3];
arr[0] = 0;

let mut vec = vec![1, 2];
vec.push(3);
vec[0] = 0;

let arr_from_vec = &vec[0.. 2];
let vec_from_arr = arr.to_vec();

```

Rust also has the [`HashMap`-type](https://doc.rust-lang.org/std/collections/struct.HashMap.html), which is the equivalent of `Dictionary<TKey, TValue>` in C# and .NET.

```
let mut map = HashMap::new();
map.insert("key", 123);

```

### Generics

Rust has [support for generics](https://doc.rust-lang.org/book/ch10-00-generics.html), just like C# does, but with a little bit more smart functionality built into the compiler. In C#, you don't always have to specify the exact generics used in some contexts, since it can be inferred. Rust has some additional functionality for this.

For both `Vec` and `HashMap`, **you don't have to specify the type explicitly** when creating the object. It will be **inferred by the first items added** and enforced to the following added items.

## Logic Flow

The `if`-statements in Rust are very similar to the ones in C#, except **you don't need parenthesis around the condition** , but it's possible to use it if you want to.

```
let mut info = String::from(title);

if rating_avg >= GOOD_MOVIE_LIMIT {
    info += " - You're in for a good movie";
} else if rating_avg < GOOD_MOVIE_LIMIT && year <= 1995 {
    info += "- Could be a classic...";
} else {
    info += "... Do you want to look for another movie?";
}

```

The `if`-statement is in itself an expression, from which you can catch the result. This means that you can **use an `if`-statement to assign a variable**. You can catch the value resulting from the `if`, the `if else`, or the `else`.

This can be used to create a _shorthand if_. This is a good replacement for the _ternary conditional operator_ (In C#: `var a = true ? b : c`), since it's not supported in Rust.

```
let classic = year <= 1995 && !title.ends_with(" II");
let text = if classic { "Potential classic" } else { "We'll see..." };

```

### Pattern Matching

Rust also has very appreciated [pattern-matching-functionality](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html). This is something that [C# has implemented](https://docs.microsoft.com/en-us/dotnet/csharp/pattern-matching) and seems to keep on improving it with every version.

Pattern-matching in Rust can do a lot more than just replace simple `if`-statements. It can also, among other things, be used to assign the result to variables. More sophisticated usages will be shown as this series goes on.

```
let info = match rating_avg {
    x if x > GOOD_MOVIE_LIMIT =>
        format!("{} - You're in for a good movie", x),
    x if x < GOOD_MOVIE_LIMIT && year <= 1995 =>
        format!("{} - Could be a classic...", x),
    _ => String::from("... Do you want to look for another movie?"),
};

```

## Standard Output

To write to the standard output, in the equivalent way of `Console.WriteLine` in C# and .NET, you use [the function `println!`](https://doc.rust-lang.org/std/macro.println.html) in Rust.

```
let hello = "Hello";
let world = "World";
println!("{} {}!", hello, world);

```

In .NET, we specify the indexes inside the placeholder-curly brackes like this: `Console.WriteLine("{1} {0}!", world, hello)`. These explicit indexes can also be used in Rust like this `println!("{1} {0}!", world, hello)`.

If you don't specify the index In Rust and just use the empty placeholder `{}`, as in the above example, **the index will be inferred**.

Just like in C# and .NET, you can [add formatting](https://doc.rust-lang.org/stable/rust-by-example/hello/print/fmt.html) to a parameter by adding a colon-character (`:`) after it and a specific flag. For debugging, it's very useful to use the [debug-formatting](https://doc.rust-lang.org/rust-by-example/hello/print/print_debug.html), which **shows the whole content of the object** in the console.

```
let obj = complex_obj();
println!("Debug: {:?}", obj);
println!("Debug pretty: {:#?}", obj);

```

### Command-Line Arguments

_Standard input_ is not as straight forward as _Standard output_ and not a one-liner like in C# and .NET with `Console.ReadLine`. So we will get more into that in future articles.

Getting access to the **command-line arguments passed to the application** is a lot easier. This can be used to quickly build a CLI-type application.

```
// Execute: cargo run "The Shawshank Redemption"

let args: Vec<String> = env::args().collect();
let first_arg =
    if args.len() > 1 { args[1].clone() } else { String::default() };
// first_arg: "The Shawshank Redemption"

```

## Functions

[Functions in Rust](https://doc.rust-lang.org/book/ch03-03-how-functions-work.html) work just like in C#, except you declare it using the `fn`-keyword. **You can specify a return-type** , but if you don't the function will just act like a `void` in C#.

```
fn is_sequel(title: &str) -> bool {
    if title.is_empty() {
        return false;
    }
    let sequel_title = title.ends_with("II");

    sequel_title
}

```

Notice the **lack of a trailing semicolon** (`;`) behind the `sequel_title`-variable at the end of the method. This makes the statement into an "expression" and Rust will **implicitly accept this as a return statement** , as long as it's the **final expression of the method**. This is why a `return` is needed in the body of the early returning `if`-statement.

This is a **feature that might look weird** when you're coming from another C-style language, but that you will see all over the place in Rust-code.

Functions are then called with their names and parameters, just like in C#:

```
fn main() {
    let sequel = is_sequel("Ghostbusters");
}

```

### Macros

In this article, you've seen the use of the `println!`-function, with the unusually looking exclamation-point (`!`) at the end. This annotation makes this a _macro_. This is a too advanced of a topic to go into in this part about basic code, so I'll just point to official [Rust Programming Language Book's section on macros](https://doc.rust-lang.org/book/ch19-06-macros.html), which summarizes:

> Macros are a way of writing code that writes other code, which is known as _metaprogramming_.

## Null-Values

The notorious [_Billion dollar mistake_](https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions) of allowing `null`-values into programming languages seems to be a **problem tackled only recently**. C# got _Nullable reference types_ with C# 8.0 in late 2019 and F# doesn't have nulls built-in at all (even if there are ways to get around it).

**Rust does not support null values.** Instead, it uses patterns to wrap around types, to help indicate if there was a value or not. The most commonly used such type is the [`Option`-type](https://doc.rust-lang.org/std/option/). It will return either a `Some`, carrying the value, or a `None`, indicating there was no object.

```
fn main() {
    let quotient = divide(123, divisor);
    match quotient {
        Some(x) if x > 10 => println!("Big division result: {}", x),
        Some(x) => println!("Division result: {}", x),
        None => println!("Division failed")
    }
}

fn divide(dividend: i32, divisor: i32) -> Option<i32> {
    if divisor == 0 {
        return None;
    }
    Some(dividend / divisor)
}

```

## Object-Orientation

[Object-orientation in Rust](https://doc.rust-lang.org/book/ch17-00-oop.html) is a point in the code-basics where **things get a bit strange** for the regular C#-developer. In C#, we are used to having a `class` that owns its fields, properties, and methods. The class is then a template for creating an _instance_ of an object.

Rust uses what they call a `struct` instead of classes, which is just a "dumb" data-structure, which contains fields, but **they do not (directly) contain properties or methods**. Instead, Rust provides the possibility to add functions to a `struct` through the `impl`-keyword.

```
pub struct Movie {
    pub title: String,
    release_year: u16,
}

impl Movie {
    // Constructor - Associated method
    pub fn new(title: &str, release_year: u16) -> Self {
        Self { title: String::from(title), release_year }
    }
    // Method
    pub fn display_title(&self) -> String {
        format!("{} ({})", self.title, self.release_year)
    }
    // Method with mutability
    pub fn update_release_year(&mut self, year: u16) {
        self.release_year = year;
    }
}

fn main() {
    let movie = Movie::new("Ghostbusters", 1984);
    println!("Movie: {}", movie.display_title());
}

```

First, we define the `struct` and its fields, where we add the `pub`-keyword to the `struct` itself and its field `title`. **All public fields are mutable** in Rust if used with the `mut`-keyword by other code. If you want to protect a field, use methods, like the `update_release_year`-function in this example.

In the `impl`-block, we have the `new`-function, which acts as a constructor. It's an associated method to the type, which is the equivalent of a `static` method in C#. It returns a new instance of the implementing type, which in this case is a `Movie`.

The function `display_title` is a **method on the instance of the object** and through the `self`-keyword in the first parameter, it can access fields and methods on the object, even non-public ones. It's the equivalent to the `this`-keyword in C#.

### Inheritance

**Rust doesn't have inheritance**. At least not in the way a C#-developer is used to it. This seems to be a very [conscious decision](https://doc.rust-lang.org/book/ch17-01-what-is-oo.html) by the Rust-team.

Instead of inheritance, Rust provides the very powerful [concept of traits](https://doc.rust-lang.org/1.9.0/book/traits.html), which allows you to extend any type, no matter if you've created the type or not. As a C#-developer it acts like of mixture of interfaces and extension-methods. We will look closer at traits in upcoming articles in the series.

## Ownership & Borrowing

You've seen the use of the ampersand (`&`) character in some places of the example-code. These indicate that the use of an object is done by reference. This is something you normally very rarely need to deal with in C# and .NET.

This relates to the unique concept in Rust of [References and Borrowing](https://doc.rust-lang.org/1.8.0/book/references-and-borrowing.html). This is **the foundational concept which enables the language's memory-safety, performance, and "[fearless concurrency](https://doc.rust-lang.org/book/ch16-00-concurrency.html)"**.

This is also one of **the hardest parts of Rust to master**. It's usually the number one complaint about the language from newcomers and a very frequent topic on Stack Overflow and other Q&A-forums.

## Primitives Comparison Table

Rust has a set of primitives that overlap quite well with the ones we're used to in C# and .NET. They are listed for reference:

| Rust | C# | Comments |
| --- | --- | --- |
| bool | bool | |
| char | char | |
| i8, i16, i32, i64, i128 | sbyte, short, int, long, (N/A) | Signed 128-bit integer not available in .NET |
| u8, u16, u32, u64, u128 | byte, ushort, uint, ulong, (N/A) | Unsigned 128-bit integer not available in .NET |
| isize, usize | (N/A) | |
| f32, f64 | float, double | .NET has the 128-bit floating-point number `decimal`, which is not available in Rust out-of-the-box |
| Array | Array | Not a primitive type in .NET. Fixed size. |
| Tuple | System.ValueTuple / System.Tuple | Not a primitive type in .NET |
| Slice | Span&lt;T&gt; | Not a primitive type in .NET |
| str | string | `str` is immutable and fixed length in Rust, but not in .NET |
| Function | Func&lt;T&gt; | Not a primitive type in .NET |

## Learn More

If you want to learn more about Rust, unrelated to its equivalent functionality in C# and .NET, you can check out the [_Learn Rust_-section](https://www.rust-lang.org/learn) of the official website, which points to multiple resources, including the [_Rust Programming Language_-book](https://doc.rust-lang.org/book/) and the [_Rust by Example_-book](https://doc.rust-lang.org/stable/rust-by-example/).

I also found the guide [_Writing Easy Rust_](https://github.com/Dhghomon/easy_rust) very helpful, because it helps to explain the more complicated Rust-concepts in easier terms.

## Summary

This article tried to lead you into the Rust-language by **relating concepts to the ones you already know in C#** and .NET.

Rust is very **similar syntactically** to other C-style languages, including C#. There are some concepts in Rust that are relatively different from what you're used to, but this can be a great way to be **inspired to solve things in new ways**. I've actually heard multiple people say that they've **rediscovered their love for programming by learning Rust**.