Showing posts with label Rust. Show all posts
Showing posts with label Rust. Show all posts

Saturday, September 23, 2023

Cross-compiling Rust for x32

While analyzing a bug in WinSafe, I had to test its compilation on the i686 platform. Without knowing, I already had it installed together with my MSVC toolchain, and I just needed a few commands in order to pull it out:

List all available toolchains:

rustup toolchain list

Which gave me:

stable-i686-pc-windows-msvc
stable-x86_64-pc-windows-msvc (default)
nightly-x86_64-pc-windows-msvc

Then choose other than the default toolchain:

cargo +stable-i686-pc-windows-msvc c

After performing all the tests, it’s a good idea to clean all the build artifacts:

cargo clean

Note that, if the toolchain is not present, it may have to be installed.

Saturday, July 29, 2023

Delegating Display/Debug trait implementations

Suppose you want implement Display and Debug traits for your struct, but the output is the same. Instead of copy & paste the implementation, you can delegate the implementation by casting the Self type to the trait type:

impl std::fmt::Debug for MyStruct {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		write!(f, "MyStruct");
	}
}

impl std::fmt::Display for HRESULT {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		<Self as std::fmt::Debug>::fmt(self, f) // delegate
	}
}

As a reminder, the Self casting can be useful in other situations.

Tuesday, December 6, 2022

The three rules of lifetime elision in Rust

While reviewing some Rust fundamentals, I stumbled across this excellent video about lifetimes. What caught my attention the most was the “three rules” of lifetime elision – a topic I had some idea about, but I’ve never seen clearly explained.

For reference, they are:

  • Each parameter that is a reference gets its own lifetime parameter;
  • If there is exactly one input lifetime parameter, that lifetime is assigned to all output lifetime parameters;
  • If there are multiple input lifetime parameters, but one of them is &self or &mut self, the lifetime of self is assigned to all output lifetime parameters.

Thursday, July 21, 2022

Sizes of Windows integral types

While developing WinSafe, it’s very common to convert the Windows integral data types to their Rust equivalent. Care must be taken, however, when it comes to pointer size, which varies according to the architecture. Since WinSafe is aimed to both 32 and 64-bit Windows, I must pay attention.

For reference, below is the table I’m using to figure out the sizes:

Signed C Signed Rust Unsigned C Unsigned Rust 32-bit 64-bit
CHAR
 
 
i8 UCHAR
BYTE
BOOLEAN
u8 8 bit (1 byte)
SHORT
 
 
i16 USHORT
WCHAR
WORD
u16 16 bit (2 byte)
BOOL
INT
LONG
 
i32  
UINT
ULONG
DWORD
u32 32 bit (4 byte)
INT_PTR
LONG_PTR
LPARAM
 
isize UINT_PTR
ULONG_PTR
WPARAM
SIZE_T
usize 32 bit (4 byte) 64 bit (8 byte)
LARGE_INTEGER
LONG64
LONGLONG
 
 
i64 ULARGE_INTEGER
ULONG64
ULONGLONG
DWORD64
DWORDLONG
QWORD
u64 64 bit (8 byte)

The table above is an extension of this one.

Monday, May 23, 2022

Cross-compiling Rust in Windows

After making a lot of confusion with Rust cross-compiling, I finally managed to compile WinSafe x32 programs in my Windows x64. The root of the misunderstanding is that, in order to cross compile, you must have the following installed:

  • MSVC build tools;
  • Rust toolchain;
  • Rust target.

Toolchain relevant commands:

rustup toolchain list
rustup toolchain install stable-i686-pc-windows-msvc
rustup toolchain uninstall stable-i686-pc-windows-msvc

Target relevant commands:

rustup target list | grep installed
rustup target add i686-pc-windows-msvc --toolchain stable
rustup target remove i686-pc-windows-msvc

To verify if you program has any linker issues, build and run:

rustup run stable-i686-pc-windows-msvc cargo run
rustup run stable-x86_64-pc-windows-msvc cargo run

Then finally the program can be built for release with:

RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target i686-pc-windows-msvc
RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-pc-windows-msvc

I only found all that stuff after posting a question on StackOverflow and receiving this comment. The documentation was completely absent in providing any useful information.

Tuesday, December 14, 2021

Install and uninstall Rust nightly

When developing the full-module refactoring of WinSafe, I found that it’s possible to tag the items with their respective required Cargo features. However, as many things in Rust, this is still available only in the nightly toolchain.

I want to make WinSafe available for the stable toolchain, so I needed to install nightly just to see how that stuff worked out, and then uninstall it after the tests.

Rust nightly can be installed and uninstalled with the following commands:

rustup install nightly
rustup toolchain remove nightly

To see which toolchains are installed:

rustup show

Thursday, December 2, 2021

RAII guards in Rust

While experimenting with scope rules in Rust, I ended up implementing a recurrent idea I had – a RAII guard that runs a function when the object goes out of scope. I’m well aware of scopeguard crate, but it has some stinky unsafe bits in its implementation.

I was never able to properly implement it, probably because my Rust skills weren’t good enough, but now I did. And I’m rather satisfied with it:

use std::ops::Deref;

/// Returns the object wrapped in a way that the associated closure will run
/// when it goes out of scope.
pub struct Guard<T, D: FnOnce(&mut T)> {
	asset: T,
	dropper: Option<D>,
}

impl<T, D: FnOnce(&mut T)> Drop for Guard<T, D> {
	fn drop(&mut self) {
		self.dropper.take()
			.map(|d| d(&mut self.asset));
	}
}

impl<T, D: FnOnce(&mut T)> Deref for Guard<T, D> {
	type Target = T;

	fn deref(&self) -> &Self::Target {
		&self.asset
	}
}

impl<T, D: FnOnce(&mut T)> Guard<T, D> {
	/// Creates a new `Guard` object.
	pub fn new(asset: T, dropper: D) -> Guard<T, D> {
		Self { asset, dropper: Some(dropper) }
	}
}

I though about writing stuff like this in WinSafe:

impl HWND {
	pub fn GetDC(self) -> WinResult<Guard<HDC, impl FnOnce(&mut HDC)>> {
		Ok(Guard::new(
			self.GetDC()?,
			move |hdc| {
				self.ReleaseDC(*hdc).expect("Guard crash.");
			},
		))
	}
}

But then there are big implications like CreateMenu, which may or may not require a DestroyMenu call. These dubious behaviors are very unsettling, and many questions arise:

  • Should I write another method, with a _guarded suffix?
  • Mark the unguarded original as unsafe?
  • In the example above, self is copied into the closure – what if it’s still zero?

So by now I believe I’ll leave everything as it is, and let the defer-lite crate at hand when needed.

Still, I greatly miss the idiomatic defer in Go.

Thursday, November 25, 2021

Bash script to deploy my own Rust tools

While developing my own personal tools in Rust – which seems to be “the one” language of my own stuff now, after the disappointment of the consistent Go crashes –, I often need to compile and replace the current *.exe tool. The compile line I use is rather long, plus I want to standardize the steps, so I put out a shell script to automate the tasks.

This shell script must be placed at the project root.

EXE=program-name.exe

echo "Compiling $EXE..."
RUSTFLAGS="-C target-feature=+crt-static" cargo build --release --target x86_64-pc-windows-msvc

echo "Replacing old $EXE..."
mv ./target/x86_64-pc-windows-msvc/release/$EXE /d/Stuff/apps/_audio\ tools/.

echo "Cleaning up..."
rm -rf ./target/release
rm -rf ./target/x86_64-pc-windows-msvc

echo "Done."

I’m not versioning this script because it contains the directories on my computer, plus the command line itself is commented in the Cargo.toml file.

Friday, November 5, 2021

Static linking of the C runtime in Rust

By default, Rust on Windows with MSVC toolchain compiles to a dynamically linked C runtime, apparently following the default configuration on Visual Studio, which uses the /MD compiler flag. In order to use static linking for the C runtime, we must change this to /MT, something I’m used to do in my C++ Visual Studio projects.

I found notes about Rust linkage, which hints us specifically about these /MD and /MT switches, although not citing them explicitly. The way to specify static linking is through RUSTFLAGS environment variable:

RUSTFLAGS='-C target-feature=+crt-static' cargo build --release --target x86_64-pc-windows-msvc

Which is rather ugly, let me say. At least it appears to work, and this is the command line I’m using for my release builds now.

Sunday, July 11, 2021

Accepting both Vec or slice of String or &str in Rust

In whathever programming language, it’s rather common to have a function which accepts an array of strings. Specifically in Rust, there is the notion of slice, which can be used as a reference to a Vec. And also there is the notion of &str, which can be used as a reference to String. Putting both concepts together as an argument is not trivial, though.

Turns out I found a way to make it work by using the AsRef trait – as many things in Rust, traits lead the way. By using generics, it becomes possible:

fn foo<S: AsRef<str>>(names: &[S]) {
	for name in names.iter().map(|s| s.as_ref()) {
		println!("{}", name);
	}
}

Or alternatively using the impl keyword for the trait bound:

fn foo(names: &[impl AsRef<str>]) {
	for name in names.iter().map(|s| s.as_ref()) {
		println!("{}", name);
	}
}

The function above accepts both Vec and slice of String or &str. But notice how, in order to retrieve the string itself, the as_ref() method must be called.

Thursday, July 1, 2021

Missing the ternary operator in Rust

Just out of curiosity, I wrote a Rust macro similar to Visual Basic’s IIf function:

macro_rules! iif {
	($cond:expr, $iftrue:expr, $iffalse:expr) => {
		if $cond { $iftrue } else { $iffalse }
	};
}

Usage:

let n = iif!(1 == 1, "yes", "no");

Close to the ternary operator, which I’m sorely missing in these Rust days.

Wednesday, June 2, 2021

Go’s defer in Rust

One of my all-time favorite features of Go is the defer mechanism. It’s so incredibly simple, and so incredibly ingenious and useful. The fact the deferred statements are called during a panic unwind makes me want to use it in other languages, like Rust.

As for Rust, I’m aware of the scopeguard crate, but it seems to be an overkill to such a simple concept. Plus, it does some fishy workaround to make the FnOnce.

So I decided to write my own Rust version of the defer mechanism, which turns out to be a macro that hides the creation of an object. This object holds a FnOnce which runs when at the end of the scope via Drop trait:

/// Internal struct used by the `defer!` macro.
pub struct Defer<F: FnOnce()> {
	func: Option<F>,
}

impl<F: FnOnce()> Defer<F> {
	pub fn new(func: F) -> Self {
		Self { func: Some(func) }
	}
}

impl<F: FnOnce()> Drop for Defer<F> {
	fn drop(&mut self) {
		self.func.take().map(|f| f());
	}
}

/// Defers the execution of a block until the surrounding scope ends.
macro_rules! defer {
	( $($tt:tt)* ) => {
		let _deferred = crate::defer::Defer::new(|| { $($tt)* });
	};
}

Usage is straightforward:

#[macro_use] mod defer; // considering defer.rs at crate's root

fn main() {
	defer! { println!("hi"); }
}

Edit: I published the code above as a crate: defer-lite.

Wednesday, March 31, 2021

Fixing Cargo authentication on GitHub

Today, while performing tests before publishing the first version of WinSafe, I stumbled across a problem I had with Go a few weeks ago. It was caused because GitHub no longer accepts user/password authentications, so everything must be done via SSH. I solved the problem by adding some lines to my ~/.gitconfig file.

But Cargo still cannot fetch packages.

After a lot of digging, I found a setting that finally worked. It involved adding another filter to ~/.gitconfig, which now looks like this:

# Force SSH instead of HTTP
# https://stackoverflow.com/a/27501039/6923555
[url "ssh://git@github.com/"]
	insteadOf = https://github.com/
	
# But crates.io needs to pass
# https://github.com/rust-lang/cargo/issues/8172#issuecomment-659066173
[url "https://github.com/rust-lang/crates.io-index"]
	insteadOf = https://github.com/rust-lang/crates.io-index

The “crates.io” directory was updated and I could use an external crate, something that will be needed when I publish my own.

As a side note, all downloaded crates are stored in ~/.cargo/registry/ directory, which can be safely wiped out to clean the cache.

Tuesday, January 5, 2021

Downcasting boxed errors in Rust

Today I was trying to write polymorphic error handling in Rust, something achievable with dynamic_cast in C++, and incredibly easy in Go. In Rust, of course, it’s overcomplicated.

After a couple frustrating hours of searching with no success, I stumbled upon the downcast_ref method of the Error trait, which finally allowed me to write what I wanted. Oddly enough, I couldn’t find this solution anywhere.

Note that the String concrete type cannot be retrieved.

Playground link here.

Friday, May 29, 2020

Rust macro to clone many objects at once

When working at my forthcoming Rust Win32 library, I was frustrated with the unergonomic task of cloning all the objects before referencing them inside a closure. The basic problem goes like this:

let a = a.clone();
let b = b.clone();
stuff.do_something(|| {
  a.foo();
  b.bar();
});

It would be nice to have some kind of syntactic sugar. Then at some point I saw an example that led me to use destructuring, very familiar from my JavaScript experience:

let (a, b) = (a.clone(), b.clone());
stuff.do_something(|| {
  a.foo();
  b.bar();
});

After that, it came to my mind that I could write a macro for that. After about a half hour wrapping my head around the concept, I finally came up with a macro to clone many objects at once:

macro_rules! clone {
  ( $($obj: expr),+ ) => {{
    ( $( $obj.clone() ),+ )
  }};
}

Which finally allowed me to write the most sugary version of all:

let (a, b) = clone!(a, b);
stuff.do_something(|| {
  a.foo();
  b.bar();
});

Testable in the Rust Playground.

Thursday, January 9, 2020

What types are heap-allocated in Rust

Today I stumbled on this question regarding which standard Rust types do alloc in the heap. I have this question popping in my head quite often, so here are the heap-allocated ones:

  • Box, simple heap allocation;
  • Rc, reference counter;
  • Arc, thread-safe atomic reference counter;
  • collections.

Reference to the answer can be found here.

Monday, September 16, 2019

Rust for C++ programmers in 30 minutes

From time to time I get back to studying Rust, which looks like a good alternative to the unthinkable complexity of C++ for native programming, which even after about 20 years, still upsets me tremendously. From the perspective of a seasoned C++ programmer like me, the Rust concept of lifetimes has been particularly esoteric.

Fortunately, I found a great resource online today:

30 minutes of Introduction to Rust for C++ programmers

It’s a very concise online book with direct comparisons between C++ and Rust codes, exactly what I was after. It explains Rust basic concepts by direct comparison to C++, what greatly helps. Although you’ll need a little over 30 minutes to properly digest it, it’s well worth reading, and I consider to be the greatest introductory material so far to C++ programmers who want to dig into Rust.

Props to vnduongthanhtung, who authored the book back in 2017.