Luca Nerlich A blog about Tech, Programming and Games.

Learning Rust - Part 2

Hey there! I am trying to learn rust. In this part, I am creating a simple application window using opengl and sdl2.

Thanks to Nerijus Arlauskas and his extremely helpful blogpost.

All other parts of this series



SDL Installation Windows/Mac

Your Cargo.toml should look as follows

name = "opengl-test"
version = "0.1.0"
authors = ["Firstname Lastname <>"]
build = ""

sdl2 = "0.31.0"
gl = "0.11.0"

Noticed the line build = ""? This is a rust build script. In our case, we use it to load windows specific stuff.

MacOS - Homebrew

As almost always, its easier to use a Unix OS ;-)

Use/install Homebrew and run the following command. You should be good to compile.

brew install sdl2

Windows 10

The following excerpt is copied from the rust-sdl2 repository. You need to download archives for mingw1 and vc2. Extract these, and copy specific files inside corresponding folder on the same level as your Cargo.toml.

SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\i686-w64-mingw32\bin 		-> 	gnu-mingw\dll\32
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\x86_64-w64-mingw32\bin 	-> 	gnu-mingw\dll\64
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\i686-w64-mingw32\lib 		-> 	gnu-mingw\lib\32
SDL2-devel-2.0.x-mingw.tar.gz\SDL2-2.0.x\x86_64-w64-mingw32\lib 	-> 	gnu-mingw\lib\64\SDL2-2.0.x\lib\x86\*.dll	 		-> 	msvc\dll\32\SDL2-2.0.x\lib\x64\*.dll 			-> 	msvc\dll\64\SDL2-2.0.x\lib\x86\*.lib	 		-> 	msvc\lib\32\SDL2-2.0.x\lib\x64\*.lib	 		-> 	msvc\lib\64

Your folder structure should look like this:


Create your file with the following content:

use std::env;
use std::path::PathBuf;

fn main() {
    let target = env::var("TARGET").unwrap();
    if target.contains("pc-windows") {
        let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let mut lib_dir = manifest_dir.clone();
        let mut dll_dir = manifest_dir.clone();
        if target.contains("msvc") {
        else {
        if target.contains("x86_64") {
        else {
        println!("cargo:rustc-link-search=all={}", lib_dir.display());
        for entry in std::fs::read_dir(dll_dir).expect("Can't read DLL dir")  {
            let entry_path = entry.expect("Invalid fs entry").path();
            let file_name_result = entry_path.file_name();
            let mut new_file_path = manifest_dir.clone();
            if let Some(file_name) = file_name_result {
                let file_name = file_name.to_str().unwrap();
                if file_name.ends_with(".dll") {
                    std::fs::copy(&entry_path, new_file_path.as_path()).expect("Can't copy from DLL dir");

This will create and copy the needed .dll files next to your Cargo.toml file during build time. Copy these to the directory where your .exe file might be placed in the future.

Executing cargo run should now successfully compile and run your program.

Initializing SDL and creating a Window

Simple DirectMedia Layer

[…] a cross-platform development library designed to provide low level access to audio, keyboard, mouse, joystick, and graphics hardware via OpenGL and Direct3D […]

As nercury described in his post, we need to instantiate the sdl context and use it to create a video subsystem with which we can open a window.


    let sdl_context: Sdl = sdl2::init().unwrap();

//    initialize video_subsystem
    let video_subsystem: VideoSubsystem =;

//    lets use it and create a window
    let window: Window = video_subsystem
        .window("Window", 800, 600)

    let gl_context: GLContext = window.gl_create_context().unwrap();

//    load gl to forward our opengl calls to the driver.
    let gl = gl::load_with(|s| video_subsystem.gl_get_proc_address(s) as *const std::os::raw::c_void);

    unsafe {
//        set color to blue
        gl::ClearColor(0.3, 0.3, 0.5, 1.0);

We will then open a potentially infinite loop which will handle all user input and window content.


//    create an EventPump to receive Events for n windows.
    let mut event_pump: EventPump = sdl_context.event_pump().unwrap();

//    label for this loop. Allows us to break free from inside the for loop.
    'main: loop {
//        an underscore as a variable prefix silences compiler warnings
        for event in event_pump.poll_iter() {
//            handle quit event
            match event {
//                pattern match for event 'Quit'. Allows the window to be closed.
                sdl2::event::Event::Quit { .. } => break 'main,
                _ => {}
//            handle user input here.
        unsafe {
//            update buffer/screen


Ive also create a small file and placed the above find_sdl_gl_driver function in it.

extern crate sdl2;

/// Find and return the OpenGL driver from SDL.
pub fn find_sdl_gl_driver() -> Option<u32> {
    for (index, item) in sdl2::render::drivers().enumerate() {
        if == "opengl" {
            return Some(index as u32);

Find the complete files here:

If implemented correctly, running our app will result in an empty, resizable and closable window.


Thats it for now. The next post will place content in this window ;-)

Tutorials / Downloads


  1. sdl2_mingw.tar.gz ~ 9.8mb 

  2. ~ 1.8mb 

Learning Rust - Part 1

Hey there! I am trying to learn rust. Follow along, try it out yourself and maybe learn a thing or two.

All other parts of this series




my git repo

Im working / learning on Windows 10 using CLion as well as MacOS - which is a lot handier for commandline tasks ;-)

Rust / Cargo Installation

  1. Download rust and follow the commandline instructions.
  2. Install the gnu toolchain
    • rustup install stable-gnu
  3. Set the gnu toolchain as your default
    • rustup set default-host x86_64-pc-windows-gnu

Debugging rust on Windows

The easiest solution for a working debugging environment on windows, is to install and use cygwin.

Download the main installation .exe and during the wizard, select and download/install at least the following four packages:

If you are using CLion, your settings should look similar to this: clion_cygwin_toolchain Debugging works regardless of the unsupported version warning.

To create this toolchain, click on the plus icon and select cygwin. If you have installed cygwin in its default location, CLion will autodetect all necessary settings.

Useful Commands

  1. cargo run
  2. cargo build
  3. cargo build –release

Basic Syntax

Functions and import

The main() function is rusts application entry point. mod imports the file loops::run() calls the run() method from the file.


mod loops;

fn main() {

pub fn run() {
    let mut count = 0;

//    print until count equals 10, then break.
    loop {
        count += 1;
        println!("{}", count);

        if count == 10 {

Basic print outs

//    print to console
    println!("We are using curly braces to print variables / values other than strings. Escape curly braces via double curly brace.");

//    using {} as format placeholders similar to java LOG.xx
    println!("{} is from {}", "luca", "hamburg");

//    we can also use numbers to refer to specific given values
    println!("{0} is from {1}", "luca", "hamburg");

//    we can also name the placeholder
    println!("{name} is from {city}", name = "luca", city = "hamburg");

//    we can also parse the incoming variable to binary, hex or octal e.g
    println!("Binary: {:b}, Hex: {:x} Octal: {:o}", 1000, 1000, 1000);
//    Binary: 1111101000, Hex: 3e8 Octal: 1750

Basic var usage

//    vars are always immutable.
    let name = "Luca";
//    type inferred to &str -> string

    let immutable = "immutable";
    let mut growable = String::from("groable");

//    adding `mut` -> makes it mutable.
    let mut age = 25;
//    type inferred to i32 -> 32bit signed integer

    println!("Name: {}", name);
    println!("Age is: {}", age);

    age += 1;
    println!("And now the age is: {}", age);

    const ID: i32 = 001;
//    type manually set to i32 -> 32bit signed integer

//    Tuples
    let person: (&str, &str, i8) = ("firstname", "lastname", 25);
    println!("{} {} {}", person.0, person.1, person.2);

    println!("Printing multiple vars using the debug placeholder :? - {:?}", (name, age));

//    Conditionals
    let height = 10;

    if height >= 10 {
        println!("Height: {} is ok. You can pass.", height);

Basic types

//    default type -> i32
    let integer = 1;

//    default type -> f64
    let float = 1.1;

//    manually set type -> i64
    let integer_large: i64 = 1233456789;

//    Boolean
    let is_alive = true;

    println!("Printing multiple vars using the debug placeholder :? - {:?}", (integer, float, is_alive));

//    Char
    let emoji = '\u{1F600}';
    println!("We can even print unicode emojis: {:?}", emoji);
//    '😀'    
//    find max sizes - signed
    println!("Max i8: {}", std::i8::MAX);
//    127
    println!("Max i16: {}", std::i16::MAX);
//    32767
    println!("Max i32: {}", std::i32::MAX);
//    2147483647
    println!("Max i64: {}", std::i64::MAX);
//    9223372036854775807
    println!("Max i128: {}", std::i128::MAX);
//    170141183460469231731687303715884105727

Unsigned integer types cannot hold negative values.

//    find max sizes - unsigned
    println!("Max u8: {}", std::u8::MAX);
//    255
    println!("Max u16: {}", std::u16::MAX);
//    65535
    println!("Max u32: {}", std::u32::MAX);
//    4294967295
    println!("Max u64: {}", std::u64::MAX);
//    18446744073709551615
    println!("Max u128: {}", std::u128::MAX);
//    340282366920938463463374607431768211455


//    array of exactly 5 i32.
    let numbers: [i32; 5] = [1, 2, 3, 4, 5];
    println!("{:?}", numbers)
//    get size in memory. Pass reference via '&'.
//    i32 -> 4 bytes. *5 arrays size -> 20bytes.
    println!("Size in mem: {} bytes.", std::mem::size_of_val(&numbers));
//    Size in mem: 20 bytes.

//    slice array
    let slice_part: &[i32] = &numbers[0..2];
    println!("Slice: {:?}", slice_part);
//   Slice: [1, 2]


    let mut numbers: Vec<i32> = vec![1, 2, 3, 4, 5];

//    reassign value
    numbers[1] = 99;

//    add on to vector

//    remove last entry from vector

//    print entries in for loop via iterator.
    for number in numbers.iter() {
        println!("Entry: {}", number);
//    loop and multiply values by 2.
    for entry in numbers.iter_mut() {
        *entry *= 2;
    println!("Entry: {:?}", numbers);
//    Entry: [2, 198, 6, 8, 10, 12]


    let mut count = 0;

//    print until count equals 10, then break.
    loop {
        count += 1;
        println!("{}", count);

        if count == 10 {

Tutorials / Downloads

Thanks for reading!


Reading unique page visits from the access.log

Hey there! In this post, I am going to quickly explain, how one can find the count of unique visitors on any server running nginx.

This how-to has been tested on Ubuntu Server 18.04.2 LTS.

To achieve this goal, we are going to use the tool Visitors. We will have to download the sourcecode, compile it to a usable program and feed it our access.log1.

Lets go.

  1. Create a new directory where you want to place visitors.
  2. cd into this directory and download the sourcecode. At the time of writing, the current version is 0.7.

     cd ~/your/path && wget
  3. This archive needs to be unzipped. Do this by using either my extract alias (see Useful .bash_profile and .gitconfig settings), or the following command:

     tar -xzvf visitors-0.7.tar.gz
  4. The sourcecode needs to be compiled.

    • If make fails or you are unable to execute it, your machine needs the essential build tools.

        # as sudo
        apt-get install build-essential
  5. We are now able to let visitors parse the access.log1 and generate a report.html file.

     ~/your/path/visitors_0.7/visitors /var/log/nginx/access.log > report.html

If done correctly, Visitors will have written its report into the report.html file inside its root folder. cd ~/your/path/visitors_0.7/.

Since you are most likely on a commandline only machine, you will need a handy solution to view this .html file. For this, we will be using Lynx - a text only webbrowser.

All common distributions will have Lynx in its repository. Therefore one can easily install it via the following command:

apt-get install lynx

Feeding Lynx our report.html, gives you the opportunity to step through the report, using your keyboard.

The page will look similar to this: lynx example Text written in Green, represent links.

Using your keyboard, right will follow this link, whereas left will go back. up and down let you navigate your cursor. Quit with Q.

As you can see in the above screenshot, lynx will analyse and group its results by day, month and pages as well as other files. Feel free to explore!

Thanks for reading!



  1. Default location /var/log/nginx/access.log  2