One example:
The (awesome) bumpalo crate that you link to gets around this by providing ported implementations of Vec and String, but if you need e.g. a custom-allocated HashMap, you need to implement it yourself.
Creating an allocator is inherently possible. Using that allocator with the rest of the ecosystem is currently not stable, as you point out.
As a sometimes Rust embedded developer, I look forward to both parameterized allocators and fallible allocation.
For example, I've been (slowly, given limited free time) working on a SQLite binding for Zig. SQLite has its own allocator, which I've wrapped like so:
const std = @import("std");
const Allocator = std.mem.Allocator;
const ArrayList = std.ArrayList;
// This is all ripped from Zig's C allocator, with adaptations to point to
// SQLite's allocator instead.
pub const allocator = &allocator_state;
var allocator_state = Allocator{
.reallocFn = realloc,
.shrinkFn = shrink,
};
fn realloc(self: *Allocator,
old_mem: []u8,
old_align: u29,
new_size: usize,
new_align: u29) ![]u8 {
std.debug.assert(new_align <= @alignOf(c_longdouble));
const old_ptr =
if (old_mem.len == 0) null else @ptrCast(*c_void, old_mem.ptr);
const buf = sqlite3_realloc64(old_ptr, @intCast(u64, new_size))
orelse return error.OutOfMemory;
return @ptrCast([*]u8, buf)[0..new_size];
}
fn shrink(self: *Allocator,
old_mem: []u8,
old_align: u29,
new_size: usize,
new_align: u29) []u8 {
const old_ptr = @ptrCast(*c_void, old_mem.ptr);
const buf = sqlite3_realloc64(old_ptr, @intCast(u64, new_size))
orelse return old_mem[0..new_size];
return @ptrCast([*]u8, buf)[0..new_size];
}
And later, to actually open a database (where we need an allocator to add a null byte to a Zig string to satisfy SQLite's expectations for a database name, so we might as well use SQLite's)¹: pub fn open(name: []const u8) !Database {
// "allocator" here being the const-defined one above
var cstrName = try std.cstr.addNullByte(allocator, name);
defer allocator.free(cstrName);
var db: Database = undefined;
var rc = sqlite3_open(cstrName.ptr, &db);
if (rc == SQLITE_OK) {
return db;
} else {
_ = sqlite3_close(db);
return errorCode(rc);
}
}
Of course, it'd be even better if such a wrapper actually used SQLite's own support for custom allocators to follow the Zig stdlib convention of allowing the user to supply one's own allocator. Going the other way seemed like a reasonable short-term choice, though, and it was a good enough excuse for me to learn the ropes on custom allocators (even if most of the code ultimately came from Zig's own C allocator).----
¹: It turns out that this specific example will apparently be entirely unnecessary in future versions of Zig; the master branch documentation seems to do away with "Zig strings" v. "C strings", instead just making strings null-terminated by default (using what looks to be new support for "sentinel-terminated" pointers/arrays/slices). Looks like I've got some work to do :)
So I guess it is not a "low level"/"systems".
But Game Boy Advance games don't really fit that description. GBA games don't accept untrusted input, and nothing bad happens if they're "compromised". (Like, when people discovered arbitrary code execution in Super Mario World, no one was worried about the security implications.) So languages like C or Zig that let you cowboy values directly into specific memory locations can be a better choice.
I'm excited about Zig in particular because the mission statement seems to be "C but nicer" -- you get the same basic programming model, but with things like instance methods, generic types, better macros, arbitrary-bit-integer types and a "crash when hitting undefined behavior" compile mode.
It can make debugging your program a lot harder :)
You have a very narrow definition of untrusted input.
I would say why I really like Zig is because the minimalism of C is still there in the syntax and it makes a whole lot of very smart choices to keep this simplicity while being so much more expressive than C.
If you really need some Rust pro's I would say it is great as the language that finally brings more of ML and functional ideas into high performance programming. Most languages nowadays have fp things but Rust has it very deep into its core. Other than that it is great to see proof systems reaching the mainstream and the security guarantees its ownership system gives is great.
Swift fills that slot, but only on Apple platforms. Linux hardly has portable support even for file access and Windows will get supported some day, and most 3rd party libraries assume Apple anyway.
D has a great community, but still found lacking in some areas.
.NET AOT compiled with the learnings taken from System C# (7.x - 9) is the closest for my daily coding activities.
Ideally Delphi like with automatic memory management.
Or maybe having a Rust IDE with visual representation of lifetimes would already be productive enough.
(I tried to install some Haskell GUI libraries, but that's very difficult on Windows. I eventually got haskell-gi to install, but as far as I could tell, it had no support for status icons.)
Qt does a pretty good job for C++ and Python, but it is not native if that is what you're looking for.
JavaFX would be a good approach if you are into JVM languages, but you might need to do some JNI wrappers to call stuff like system tray, as just like Qt it does its own rendering.
Some corporations, go the route of having common code for the business logic, and then create an abstraction layer for stuff like system tray, UI widgets and stuff. Although is does require additional development cost.
So you call something like show_tray_message "New Email" and have an implementation for each OS that you care about.
If you are using Haskell, have you tried Haskell Platform?
I've tried Haskell Platform. I had a lot of path problems in trying to install gtk2hs - it wasn't always obvious where things were (nor that Stack came with its own mingw, so I got my own, and that caused more problems... eventually I wiped everything Haskell-related and started from scratch with Platform), and there's also a known problem with gtk2hs where you have to edit some cabal files to make installation work on Windows, so I would've had to do local installs. I think I got gtk2hs to install at one point, and even to compile their sample hello-world program, but trying to actually run it gave a litany of obscure errors. WxHaskell has an installer batch file, but it just spewed errors at me. I spent about a week trying to install various GUI libraries, and the only one I tried that I managed to get working was gi-gtk... which doesn't have system tray support.
I'm hoping to avoid C++ and Java, but if I have to bite that bullet, I will.
Worth noting that the Fman Build System (fbs)¹ gets you most of the way there with Python and Qt5, allowing you to "freeze" a PyQt5 application and automatically generate a Windows installer. I don't recall of the top of my head if there's a way to condense that into a single standalone executable, but it certainly produces a single folder by default (which you can stick anywhere, like any other "portable" Windows app).
I haven't used this in production yet, but testing it has been promising enough, and I'm using this for my next desktop development project.
I don't do much desktop programming, but I think that the system tray has had support in Java for many years now:
https://docs.oracle.com/javase/tutorial/uiswing/misc/systemt...
And that whole JDesktop stuff from Sun is unfortunately unmaintained since years (still left to rotten under Sun's stewardship).
I like both platforms anyway.
Doesn't Nim check those boxes? Didn't try it personally but I was instantly reminded of Nim when I read your comment.
Then C is not portable Assembly that keep being talked about, so unless you stick with care to ISO C, there will be UB and compiler specific behaviour creeping into backend.
Even languages that use LLVM suffer from this, because its design is tainted by clang, and for certain bitcode sequences, it assumes C semantics.
This broke a couple of Rust optimisations. Not sure if they already got around fixing this.
Then C does not really expose the CPU features, so if you want to actually access everything. Either you need to support inline Assembly as well, or ship Assembly alongside C anyway.
Having C as backend is only useful as means to bootstrap an eco-system to not spend too much time implementing a backend.
However when a language matures, it is time to move on, just like C++ and Objective-C did.
1. https://www.youtube.com/watch?v=hBCsWEQ_asM&list=PLviMr_WImM...
I had looked at his 2018 Advent repo and was disappointed to find that he simply pasted his input directly into the code, whereas I like to compile generic binaries that can read inputs from the command line. However, it looks like his 2019 solutions for days 1 and 2 at least do read input from a file, so this year looks like it'll be a better starting point.
0.5.0 Documentation https://ziglang.org/documentation/0.5.0/#Introduction
root std library https://github.com/ziglang/zig/tree/master/lib/std
examples of writing and reading to a file https://github.com/ziglang/zig/blob/master/lib/std/event/fs....
There are also a ton of projects that the creator has done with Zig, you can check those out for more examples as well.
https://github.com/andrewrk?utf8=%E2%9C%93&tab=repositories&...
Looks like he has an advent of code repo too.
Documentation for Zig isn't great, but it's become much easier now that the standard library is searchable: https://ziglang.org/documentation/master/std