Lädt...


🔧 Zooming to Zig : Tips, Tricks, and Trip-Ups


Nachrichtenbereich: 🔧 Programmierung
🔗 Quelle: dev.to

Cover photo (C) Tai Kedzierski

Scripting languages and System languages are two parts of a vast ecosystem. I wanted to cross to the other side to see what life is like there...

Having trawled through a lot of reference material, I found some common-use concepts still lacking for the beginner, so this aims to stand as a jump pad for anyone else from a python/scripting starting out with systems languages in general, and Zig in particular.

Zig? ⚡

Zig is the ace new kid on the block and, whilst still in its early days, is proving that it has a lot to offer. The release of the bun JS/TS runtime launched its name to the fore and developer interest has increased rapidly over the last two years - it debuted on StackOverflow's 2023 survey at an impressive initial 39.5% on the admired rating, and the following year rose to 73.8% - well ahead of C's 47.4% , and only just tailing Rust's 82.2%.

I myself work predominantly with Python, a garbage-collected scripting language with lots of QOL features and have been dabbling with Go. Both occupy a same space of wanting to facilitate rapid development, with the latter being much more stripped back and compiling to native machine code, a native string type, and a non-managed package dependency system.

I've coded in Go a little bit, and was considering basing my next efforts there, as it feels much more approachable for a script language specialist, but I felt it would be a much better horizon-widener to dive deep into systems programming. I tried to get on board with Rust, but felt its hype and promises ended up not delivering enough against the difficulty to work with it. The worst for me was the amount of code nesting its management entailed, especially managing error cases against option-matching.

Now having completed a simple project in Zig, I've come to appreciate a number of subtleties that systems programmers face that we take for granted in the GC/scripting world.

Documentation and Learning Resources

There are a number of good learning resources for the basics - the order in which I stepped through was to first read the language reference. Following some advice I saw for Rust, I read through the documentation first whilst resisting the temptation to experiment too much.

I then started looking through the Ziglings excercises one by one, and finding them to be pretty good at gently easing the learner through basic concepts. After exercise 25 or so, I decided that perhaps I didn't need to go through the rest of it and decided to rewrite my Go project Os-Release Query . I had written it in Go as a beginner in about two or three sessions to completion, so I reckoned it was a simple enough to proceed with. It took me about two weeks with several sessions of learning and stumbling.

As I contended with a few teething difficulties, I found myself frequently going to Zig.guide for simple examples and explanations of usage - specifically the ArrayList and Allocators pages, the former being key for doing general file reading tasks. My first mistake was to think about string arrays similarly as in Python or in Go (see "Strings and ArrayLists" below).

Finally, sensing I was needing a bit more guidance, I found myself stepping through several excellent videos from Dude the Builder's (José Colón) Zig in Depth series. Having read through the standard documentation, and cross-referencing against the standard library documentation, Colón's guidance was the last missing piece for getting proper progress.

And of course, web searching and StackOverflow filled in specific gaps, notably I kept coming back to a string concatenation answer on SO.

Concepts

Arrays, Slices, Strings and ArrayLists

Unless you've worked in C before, you come in for a rude awakening to discover that there is no native language construct for a "string" (beyond a double-quote notation of static literals). Most modern languages have them. All scripting languages have them in some form. Go and Rust have them. But C and Zig share the same principle: there is no string, only sequences of bytes. Whilst C has a "character" char type, Zig takes it a step further by only referencing an unsigned integer type to represent a byte.

# C
char hi[] = "hello"; 
// or even
char* hi = "hello";

# Zig
var hi:[]const u8 = "hello";

On the Zig side, note the const on the array type notation - a lot of functions which receive a "string" as parameter best makes the assumption that the data being received could be a literal - in this case, it's hard-coded and unmutable. If a function is defined as

fn myFunc(data:[]u8) { ... }

then the type cannot be a string literal, but instead requires that the item be mutable, that it can change - pointers into heap memory locations most frequently.

There are many subtleties to this, and I mainly worked against an item from Zig.news to keep myself sane.

I made an early mistake to think about loading a ASCII file lines, into an array or array of bytes, whereas in fact what I needed was an ArrayList - a dynamically growable and iterable sequence of items - of slices, which are sort of 'views' (with a pointer and length) onto arrays (a fixed-length sequence of a given type).

Stack and Heap

Stack vs Heap is a key concept in any language where you are made to manually manage memory, there's no way around it really, and coming in from GC languages, I had to be disabused of some simplistic notions.

Understanding the stack and the heap was a bit more than "heap is big and persistent/stack is small and throwaway". It also goes hand-in-hand with reasoning on passing by value or reference, which in turn is more than "did you pass through a native literal type or a pointer." Geeks for Geeks goes over the principle from a mainly C/C++ point of reference, whereas OpenMyMind.net walks through it specifically with Zig examples.

I am not in a position to explain its nuances myself as I am still learning, but the shift in understanding these was very much crucial for comprehending what I was doing in my project. I can only recommend reading up on those two sites as a starting points as mandatory reading before proceeding further.

Allocators

One of the key features of Zig is its use of allocators, which is one of the major improvements in memory management assistance we get from the compiler and its debug-mode builds. It is still possible to double-free and leak memory from improperly-written code - but using the debug build (the default build mode) causes exceedingly useful stack traces to show the origins of leaked/non-freed memory and give you an early chance of catching and remediating memory problems.

The General Purpose Allocator can be the go-to reference for pretty much anything if you just want to get started. Both Zig Guide and Dude the Builder give an excellent run down on how to use them - once you see them in use a couple of times, they usually feel mostly plug-and-play.

Structs and Methods

Unlike in C and Go, structs can come with their own dedicated functions, or methods, firmly attached to the types they are associated with. Like in Python, if the first argument is a self reference, it can be used on an instance, but without it, it can only be used from an uninstantiated struct type.

This gives the flexibility of some type-specific operations that are carried through a instance.method() notation, but avoids the pitfall of object-orientation inheritance deluges by just not permitting them.

I find this notationally and conceptually convenient, and a good introduction to the idea of "composition over inheritance" that is becoming more and more popular. Stuff that, JavaDeepInheritanceSingletonFactoryInstantiatorHelper!

Build System

The build system in Zig is interesting. I cannot claim to fully understand it much at all, except that to say I am glad I don't have to contend with Makefiles. Whether it is in fact better remains to be seen (for me), but I am going to keep exploring.

One thing I do enjoy very much however is the build.zig.zon file, and how it manages dependencies.

.{
  .dependencies = .{
    .zg = .{
      .url = "https://codeberg.org/dude_the_builder/zg/archive/v0.13.2.tar.gz",
      .hash = "122055beff332830a391e9895c044d33b15ea21063779557024b46169fb1984c6e40",
    },

    .module2 = .{
      .url = "https://site/path/item2.tar.gz",
      .hash = "1210abcdef1234567891",
    },
  },
}

Using other peoples' libs

There is no central package manager in Zig - simply point at tarball URLs online and fetch them. ZigList.org tracks libraries on Github and Codeberg (primary host of Ziglings and a preferred space for some Zig creators), and if you do publish one on either platform, it should eventually get picked up.

The hash however is not a hash of the tarball, but a cumulative hash of each exported item from the package.

Either way, there is an interesting aspect of the system: after performing zig build, any dependencies are downloaded and unpacked to ~/.cache/zig/p/<hash>/... . There are two aspects that I came up against in playing with this:

  • You cannot easily calculate the hash in advance yourself
  • If a hash exists already, then the tarball is not re-downloaded.

So when I wanted to add my second module, I needed a placeholder hash. I blindly re-used the hash from the prior module and could not figure out why it wouldn't a/ prompt me for hash re-validation on my bogus hash, and b/ didn't find the files from the second module.

Eventually after trawling the net I found two resources, I figured that I should simply copy an existing hash and then mangle the last few bytes of it. The standard documentation is admitted by the core team to not yet fully be there yet - which I understand since the dust isn't yet completely settled on the final forms of many things, and a lot is still moving about, so other peoples' blogs are a godsend...

Using a library involves adding some code in your build.zig file:

    // Find the dependency named "zg" as declared in build.zig.zon
    const dep_zg = b.dependency("zg", .{});

    // Get the `CaseData` module from the `zg` dependency
    //   and register as importable module "zg_CaseData"
    // In project code, use @import("zg_CaseData")
    exe.root_module.addImport("zg_CaseData", dep_zg.module("CaseData"));

Usually the README of the library will give you more detailed instructions and, hopefully, the hash to use as well. The above was an example for using Dude the Builder's zg library for UTF-8 string handling.

Creating your own lib

One item I created for my project was an ASCII file reader and, knowing that I would likely want to re-use that code, I decided to try to make my own library.

The most minimal snippet for creating a library in Zig 0.13.0 consists of a source directory, a build.zig.zon to declare relevant items, and a build.zig for executing the build when it is imported:

The build.zig.zon file:

.{
    .name = "my-lib",
    .version = "0.1.0",
    .paths = .{
        "build.zig",
        "build.zig.zon",
        "src",
    },
}

The build.zig file:

const std = @import("std");

pub fn build(b: *std.Build) void {
    const target = b.standardTargetOptions(.{});
    const optimize = b.standardOptimizeOption(.{});

    _ = b.addModule("my-lib", .{
        .root_source_file = b.path("src/main-file.zig"),
        .target = target,
        .optimize = optimize,
    });
}

That's it. Follow then the above for importing it.

Final thoughts

It's only the beginning for me with Zig and so far, it has been rewardingly challenging. How does it stack up to other languages I use or have considered?

bash - it's not at all in the same wheelhouse. Shell scripting excels at many things most other languages do not, including piping multiple commands together and launching system operations via commands, which is cumbersome to do, even in Python. Conversely, bash and other shell languages are horrendous for doing anything in which you want a rich data structure for, or recursion.

It also has the drawback of being extremely dependent on the runtime environment, commands installed, and others, that doing more than a few simple tasks would be a foolhardy endeavour. Believe me, I've tried.

Python - If I really just want to get things done, especially at work, Python will probably continue to be my go-to. For all its being a hundred or even a thousand times slower than native code, it makes up for in mere speed of development. For most applications that slowness tends to be of the type "it takes 0.005 seconds instead of 0.000005 to run" which is plenty for quick file conversions, automated testing, command line utilities of all kinds, and more. My current bread-and-butter is automated testing and tooling, and nothing we could think of indicated that we should consider moving away from a performance point of view.

Greater minds have said it better than I could

Go - I still aim to have a deeper look in with Go, for scenarios where either tons of data must be processed and raw execution is the bottleneck (as opposed to file I/O), or where the need is for a no-runtime-dependencies executable, for pushing onto hosts of arbitrary setups. It is not as rich syntactically as Python, but its simplicity forces much more considered programming - as far as I can tell.

Zig hits the next rung down, and as far as I am concerned, the final rung. I don't think I'm moving into embedded space any time soon, nor will I be building extreme performance solutions on my very next role, but I do intend to start considering Zig for my first-choice for CLI applications just so I can continue to sharpen my skills. One day I do intend on doing a little network-multiplayer game, and I might consider Zig for the server end, and Love2D with Lua for the front end. To each its own.

Going on this little escapade has taught me much about how to design for memory efficiency (I have not achieved it - but I know how my Python-oriented code design would not fly in a performance-oriented space).

And that's growth.

...

🍏 Road Trip Planner 4.6.08 - Road trip planner and manager.


📈 30.91 Punkte
🍏 iOS / Mac OS

🪟 Trip Planner AI Review: Should You Use It for Your Next Trip


📈 29.59 Punkte
🪟 Windows Tipps

🪟 Snip & Sketch gains zooming and more window options for Windows Insiders


📈 27.09 Punkte
🪟 Windows Tipps

🪟 Use TaoTronics Ring Light for pro-level selfies, Zooming, and streaming


📈 27.09 Punkte
🪟 Windows Tipps

📰 Zoomtopia Highlights: Zoom Zooming into Email and Calendar


📈 27.09 Punkte
📰 IT Nachrichten

🔧 How Panning and Zooming Work in a 2D top-down game


📈 27.09 Punkte
🔧 Programmierung

🔧 Zooming and Panning


📈 27.09 Punkte
🔧 Programmierung

📰 Microsoft Finally Brings Zooming to the Windows Console


📈 25.78 Punkte
📰 IT Security Nachrichten

📰 5 things you can do today to make Zooming safer


📈 25.78 Punkte
📰 IT Security Nachrichten

🎥 Zooming, Zoomie, & Zoomfest Zoo - SWN #23


📈 25.78 Punkte
🎥 IT Security Video

🎥 Zooming Alex Stamos & Building Security TestOps - ASW #103


📈 25.78 Punkte
🎥 IT Security Video

🔧 Zooming In/Out in Samsung DeX Mode


📈 25.78 Punkte
🔧 Programmierung

🐧 Firefox nightly 86.0a1 adds pinch zooming to Linux!


📈 25.78 Punkte
🐧 Linux Tipps

📰 Zooming with the Grandkids: Five Easy Video Chat Apps for the Holidays


📈 25.78 Punkte
📰 IT Security Nachrichten

📰 Top 200+ Best CMD Tricks, Tips And Hacks Of 2019 (Command-Prompt Tricks)


📈 23.83 Punkte
📰 IT Security Nachrichten

🐧 TikTok Tips And Tricks 2020 | 8 Best TikTok Tricks To Step Up Your Game


📈 23.83 Punkte
🐧 Linux Tipps

📰 Security In 5: Episode 386 - Tools, Tips and Tricks - Holiday Security Tips You Should Consider Now


📈 23.21 Punkte
📰 IT Security Nachrichten

📰 Security In 5: Episode 405 - Tools, Tips and Tricks - Security Tips For Students Back To Class


📈 23.21 Punkte
📰 IT Security Nachrichten

📰 Security In 5: Episode 525 - Tools, Tips and Tricks - Parents Tips For Video Gamers - A Video


📈 23.21 Punkte
📰 IT Security Nachrichten

📰 Security In 5: Episode 657 - Tools, Tips and Tricks - Tips To Make Office 365 MFA Better


📈 23.21 Punkte
📰 IT Security Nachrichten

🪟 7 essential tech travel tips and preparation skills for your next trip


📈 23.2 Punkte
🪟 Windows Tipps

🔧 A performant and extensible Web Server with Zig and Python


📈 22.65 Punkte
🔧 Programmierung

📰 Going on holiday? Here are our tips for a security-minded trip


📈 21.89 Punkte
📰 IT Security Nachrichten

📰 5 Tips for Making the Most Out of Your Next Business Trip


📈 21.89 Punkte
📰 IT Security Nachrichten

🐧 randomutils released (and it's written in Zig)


📈 21.34 Punkte
🐧 Linux Tipps

🔧 Zig's Power in Action: C Integration and WASM Compilation for Terrain Generation


📈 21.34 Punkte
🔧 Programmierung

🔧 Quick Zig and C String Conversion Conundrums


📈 21.34 Punkte
🔧 Programmierung

📰 Mirai-Botnetz: DynDNS bestätigt Angriff von zig-Millionen IP-Adressen


📈 20.03 Punkte
📰 IT Security Nachrichten

📰 Mirai-Botnetz: DynDNS bestätigt Angriff von zig-Millionen IP-Adressen


📈 20.03 Punkte
📰 IT Nachrichten

📰 Mirai-Botnetz: DynDNS bestätigt Angriff von zig-Millionen IP-Adressen


📈 20.03 Punkte
📰 IT Security Nachrichten

📰 Mirai-Botnetz: DynDNS bestätigt Angriff von zig-Millionen IP-Adressen


📈 20.03 Punkte
📰 IT Nachrichten

🪟 Dieser Trick räumt zig GB auf Ihrer Platte frei


📈 20.03 Punkte
🪟 Windows Tipps

📰 Microsoft lässt zig Windows-Support-Betrüger in Indien hochgehen


📈 20.03 Punkte
📰 IT Security Nachrichten

📰 Microsoft lässt zig Windows-Support-Betrüger in Indien hochgehen


📈 20.03 Punkte
📰 IT Security Nachrichten

matomo