Introduction
Rust is a general-purpose, multi paradigm, compiled programming language. It is designed to be a fast, secured and type-safe language. The language is Open-Source and developed by Mozilla Research.
According to https://en.wikipedia.org/wiki/Rust_(programming_language) -
Although its development is sponsored by Mozilla, it is an open community project.
Rust supports both Functional and Object-Oriented programming. It supports C/C++ like pointer manipulation. So what we do in C/C++ language, we can do in Rust language, but in a very different way.
Rust Language Featuring (Taken From https://www.rust-lang.org/):
- zero-cost abstractions
- move semantics
- guaranteed memory safety
- threads without data races
- trait-based generics
- pattern matching
- type inference
- minimal runtime
- efficient C bindings
In this tip, we will see how we can use this language to do GUI (Graphical User Interface) programming. As an example program, we will create a simple Window. Using Win32 API functions.
Using the Code
As far as I know, libraries are called Crates in Rust language. We are going to use various crates libraries in this project. libc
, winapi
, etc. Those crates are available in Rust crate host website (https://crates.io/).
Crates are automatically downloaded when you build your project using Cargo tool.
The Cargo is a tool for Rust language that helps in managing Rust projects. It comes with Rust official installer. The Cargo tool downloads all the dependencies needed for the project, builds dependencies and builds the project. Cargo tool helps programmer to build Rust project nicely. So we better use Cargo to make things easier.
To use the Cargo tool in our project, first we have to follow some rule. We have to create a root folder. The folder name should be your project name. Inside the root folder, we need to create an another folder called ‘src’ for keeping project source files. Because the Cargo tool expects the project source files inside a src directory and then, inside the root folder, we need to create a configuration file called ‘Cargo.toml’. The file is in TOML format which is similar to INI file but has some extra advantage.
Create the ‘Cargo.toml’ file and write the following lines of code to it:
[package]
name = "simple_window"
version = "0.0.1"
authors = [ "Your name <you@example.com>" ]
Then, add the following dependencies in the ‘Cargo.toml’ file:
[dependencies]
libc = "0.1.10"
winapi = "0.2.4"
user32-sys = "0.1.2"
kernel32-sys = "0.1.4"
First, we extern the following necessary crates in our main source file called ‘main.rs’. I assume that you are familiar with pure Win32 API functions:
extern crate kernel32;
extern crate user32;
extern crate winapi;
extern crate libc;
Then we ‘use
’ necessary types and functions from those libraries:
use winapi::windef::HWND;
use winapi::windef::HMENU;
use winapi::windef::HBRUSH;
use winapi::minwindef::HINSTANCE;
use winapi::minwindef::UINT;
use winapi::minwindef::DWORD;
use winapi::minwindef::WPARAM;
use winapi::minwindef::LPARAM;
use winapi::minwindef::LRESULT;
use winapi::winnt::LPCWSTR;
use winapi::winuser::WS_OVERLAPPEDWINDOW;
use winapi::winuser::WS_VISIBLE;
use winapi::winuser::WNDCLASSW;
use std::os::windows::ffi::OsStrExt;
use std::ffi::OsStr;
The following function is used to convert normal string
to wide string
:
fn to_wstring(str : &str) -> Vec<u16> {
let v : Vec<u16> =
OsStr::new(str).encode_wide().chain(Some(0).into_iter()).collect();
v
}
This is our window message handler function. Currently, it only processes the WM_DESTROY
message to exit our window properly on close
event.
pub unsafe extern "system" fn window_proc(h_wnd :HWND,
msg :UINT, w_param :WPARAM, l_param :LPARAM) -> LRESULT
{
if msg == winapi::winuser::WM_DESTROY {
user32::PostQuitMessage(0);
}
return user32::DefWindowProcW(h_wnd, msg, w_param, l_param);
}
You may have noticed that we have used an ‘unsafe
’ keyword in the above codes. According to https://doc.rust-lang.org/book/unsafe.html:
Rust’s main draw is its powerful static guarantees about behavior. But safety checks are conservative by nature: there are some programs that are actually safe, but the compiler is not able to verify this is true. To write these kinds of programs, we need to tell the compiler to relax its restrictions a bit. For this, Rust has a keyword, unsafe. Code using unsafe has less restrictions than normal code does.
So that’s why we have to use the unsafe
keyword.
The following function hides the Console Window since we are building a GUI application. (Actually, I don’t know how to use ‘subsytem
’ with Cargo tool! If someone knows, then please comment to improve this tip. :-> )
fn hide_console_window()
{
let window = unsafe {
kernel32::GetConsoleWindow()
};
if window != std::ptr::null_mut() {
unsafe {
user32::ShowWindow (window, winapi::SW_HIDE)
};
}
}
The following is the entry point function of our program. We create our window inside this function by using the typical Win32 GUI programming style:
fn main()
{
unsafe
{
hide_console_window();
let class_name = to_wstring("my_window");
let wnd = WNDCLASSW {
style: 0,
lpfnWndProc: Some(window_proc),
cbClsExtra: 0,
cbWndExtra: 0,
hInstance: 0 as HINSTANCE,
hIcon: user32::LoadIconW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
hCursor: user32::LoadCursorW(0 as HINSTANCE, winapi::winuser::IDI_APPLICATION),
hbrBackground: 16 as HBRUSH,
lpszMenuName: 0 as LPCWSTR,
lpszClassName: class_name.as_ptr(),
};
user32::RegisterClassW(&wnd);
let h_wnd_window = user32::CreateWindowExW(0, class_name.as_ptr(),
to_wstring("Simple Window").as_ptr(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
0, 0, 400, 400, 0 as HWND, 0 as HMENU, 0 as HINSTANCE, std::ptr::null_mut());
let mut msg = winapi::winuser::MSG {
hwnd : 0 as HWND,
message : 0 as UINT,
wParam : 0 as WPARAM,
lParam : 0 as LPARAM,
time : 0 as DWORD,
pt : winapi::windef::POINT { x: 0, y: 0, },
};
user32::ShowWindow(h_wnd_window, winapi::SW_SHOW);
loop
{
let pm = user32::GetMessageW(&mut msg, 0 as HWND, 0, 0);
if pm == 0 {
break;
}
if msg.message == winapi::winuser::WM_QUIT {
break;
}
user32::TranslateMessage(&mut msg);
user32::DispatchMessageW(&mut msg);
}
}
}
Conclusion
Rust is a nice programming language. Programmers can learn this language to enhance their skill on programming.
Hi, I'm Shah Farhad Reza. I'm a desktop and web software developer.
Recently I've developed an web based ERP (Enterprise Resource Planning) Software for a manufacturing company. The software is in use and working effectively fulfilling its goal (Alhamdulillah) - [February 10, 2023]
The areas of my expertise are the followings:
- OS Kernel developing.
- Programming language's compiler design and implement.
- Expert in C, C++ and Visual Basic and have basic knowledge on C#, D, Java.
- A few times used the Microsoft's new language F#.
- SQL Database programming.
- I've basic knowledge on lowest level programming language like assembly.
- Learning Mozilla’s Rust & Google’s GO programming language for software development.
- Code optimization for performance.
- Multi-threaded programming in C/C++ and Java.
- Know various advanced computer algorithm and have used them to develop graphics and simulation programs. Also working with Linear Algebra and keen to learn Quadratic Algebra in future.
- Graphics and Game programming (Both 2D and 3D).
Currently, I'm doing research on programming language and its compiler development. I've made various kind of software and now I want to share my experiences with people.