Skip to content

Instantly share code, notes, and snippets.

@jvns
Last active October 5, 2021 08:41
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jvns/7751946 to your computer and use it in GitHub Desktop.
Save jvns/7751946 to your computer and use it in GitHub Desktop.
Rust type tutorial

I found understanding Rust types really confusing, so I wrote up a small tutorial for myself in an attempt to understand some of them. This is by no means exhaustive. There is a types section in the manual, but it has nowhere near enough examples.

I'm not talking about managed pointers (@) at all. A lot of the difficulty with Rust types is that the language is constantly changing, so this will likely be out of date soon.

First, a few preliminaries: it's easier to play with types if you have a REPL and can interactively check the types of objects. This isn't really possible in Rust, but there are workarounds.

To start out: some help

How to get a Rust REPL

There is no working Rust REPL. However, you can use this script to approximate one -- it compiles what you put into it and prints the result. You can't use the results of what you did previously, though. Save as rustci.

How to find the type of a variable

If you want to find the type of a variable y, run:

let x: () = y;

This will generate a compiler error with the type of y. This is a hack, but as I understand it it's the best workaround for now.

For example, to find the type of a function in our fake REPL, you can do

fn f() {}; let x: () = f;

This will give us the error:

error: mismatched types: expected `()` but found `fn()` (expected () but found extern fn)

which tells us that the type of f is fn(). Yay!

The types!

Primitive types

This is an incomplete list.

Integers (signed and unsigned): int, uint, i8, i16, i32, i64, u8, u16, u32, u64

Floats: f32, f64

Booleans: bool

Primitive type examples

let x: uint = 2;
let y: u8 = 40;
let z: f32 = abc;

Vectors

There are 3 possible types for a vector of u8: [u8, ..N], &[u8], ~[u8].

[u8] by itself is not a type.

[u8, ..5] is a fixed-size vector of u8 of length 5.

Vector Examples

// Fixed size vector
let x : [uint, ..10] = [5, ..10]; // [5,5,5,5,5,5,5,5,5,5]

// Create a variable size owned vector
let mut numbers1 : ~[uint]= ~[0, 1, 2, 3, 4, 5];

// Create a variable size borrowed vector. This is also called a "vector slice".
let mut numbers2 : &[uint]= &[0, 1, 2];
let mut slice: &[uint] = numbers1.slice(0, 3);

Strings and characters

Some string types include: &str, ~str, and &'static str.

A string is represented internally as a vector of bytes. However, str by itself is not a type, and there are no fixed-size strings. You can convert any of the string types to a byte vector &[u8].

char is a 32-bit Unicode character.

String Examples

use std::option::Option;
// Static string
let hello: &'static str = "Hello!";
let hello2: &str = "Hello!";

// Owned string
let owned_hello: ~str = ~"Hello!";

// Borrowed string
let borrowed_hello = &owned_hello;

// Character
let c: char = 'a';

// Indexing into a string gives you a byte, not a character.
let byte: u8 = owned_hello[1];

// You need to create an iterator to get a character from a string.
let c: Option<char> = owned_hello.chars().nth(2);

// Switch to the string's representation as bytes
let bytes: &[u8] = owned_hello.as_bytes();

Functions

For a function fn(a: A) -> B

fn(A)->B is a type, So are &(fn(A)->B), ~(fn(A)->B), but you need to add parens right now.

You probably only want to use fn(A)->B, though.

Function type examples

fn foo(a: int) -> f32 {
    return 0.0;
}
let bar: fn(int) -> f32 = foo; 
let baz: &(fn(int) -> f32) = &foo;

Closures

The type of a closure mapping something of type A to type B is |A| -> B. A closure with no arguments or return values has type ||.

Closure type examples

let captured_var = 10; 
let closure_no_args = || println!("captured_var={}", captured_var); 
let closure_args = |arg: int| -> int {
  println!("captured_var={}, arg={}", captured_var, arg); 
  arg
};

// closure_no_args has type ||
// closure_args has type |int| -> int

fn call_closure(c1: ||, c2: |int| -> int) {
  c1();
  c2(2);
}

call_closure(closure_no_args, closure_args);

Raw pointers

For any type T, *T is a type.

@omendezmorales
Copy link

Hi @jvns,
Thanks for sharing this tutorial. I assume the repl you refer to runs only in Linux environments?

thanks for your reply

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment