Hacker Newsnew | past | comments | ask | show | jobs | submit | koito17's commentslogin

Since Japan has exactly one timezone and most services are intended for use within Japan, many Japanese programmers are unaware of timezone-related logic. I've frequently encountered Japanese sites store dates as OffsetDateTime instances (rather than UTC Instants), and have frequently seen subtle bugs, like a web app's backend returning UTC+9 timestamps, and then subsets of the frontend format the date according to the user's local time. I witnessed this last week on a fairly big concert ticket website. The lottery application datetime was simply formatting the OffsetDateTime returned by backend. Then, for some reason, the lottery result announcement datetime was being passed through a different formatting function. Two adjacent timestamps were in different timezones and there were no visual indications of this. I learned a lot about the site's API when investigating. I later reported the bug and was told the service is not intended for use outside of Japan, so they won't fix.

I've also encountered games where device local time results in bugs when certain features are gated to durations within Japanese Standard Time. For one particular game, I contacted support and the response was similarly "this service is intended for use within Japan; please ensure your device time is set to Japanese Standard Time." The support representative actually went further and told me, "if you are using this service outside of Japan, then we cannot make any guarantees on [the app's] behavior or resolve issues."

To most programmers in the US or Europe, storing all dates as UTC and localizing datetimes in the frontend may seem like "common sense", but this is essentially 異文化 to many Japanese programmers, for lack of a better way to phrase it.


I just want to display the last updated time in the corner of a blog post. Overwriting local innerText with JavaScript for that is nonsense.

Displaying relative time like “1 day ago” is especially foolish. Does that mean between 0s–23:59:59? Or 24:00:00–47:59:59? Or perhaps 24:00:00–6d 23:59:59?

When you have multiple browser tabs open and switch between them, and you see “just now” — is it really just now? You last looked at that tab an hour ago. Maybe two hours? Maybe it’s been open since yesterday.

Sometimes I wonder why the Accept-Timezone header doesn’t exist in web browser requests. Using Accept-Language to switch webpage language based on the same URL is now commonplace.

There would likely be demand for a feature where the <html> contains a Unix timestamp, and the browser renders it using the client’s local time zone:

<time epochMs="1234567890123" format="YYYY/MM/DD HH:mm:ss" />

Just write it into static HTML and you’re done. No need for the server to compute relative times, and no need to rewrite it dynamically with JavaScript.

What happened to the movement to abolish daylight saving time? A few years ago, I kept hearing that the US and EU were moving toward ending it. Yet here we are, still observing DST this year.


> Displaying relative time like “1 day ago” is especially foolish. Does that mean between 0s–23:59:59? Or 24:00:00–47:59:59? Or perhaps 24:00:00–6d 23:59:59?

> When you have multiple browser tabs open and switch between them, and you see “just now” — is it really just now? You last looked at that tab an hour ago. Maybe two hours? Maybe it’s been open since yesterday.

I have a screenshot of two Jira comments that go from "one year ago" to "one hour ago" because the two comments happened to be something like Dec 31 and Jan 2.


I am sure the "as a user, I should see relative timestamps" ticket was closed fast though! Another job well done!

So it does not take the year into consideration? Well damn.

Thanks for the context, it felt really weird to see such a "rookie" bug on the front page of HN :)

(And I mean "rookie" in the "young an innocent sense" - I vividly remember the day I was asked to displayed timestamped data for IoT devices in both local times of the machine and the user. "Yeah, should take half the day" was my answer. Ooooh to be naive again...)

I suppose the sentiment must be the same as i18n for the USA ("what do you mean there are _other_ languages ? Oh, you mean Spanish ?"), unit systems for Continental Europeans ("what do you mean 'Imperial system' is still a thing ?), and currencies for everyone ("what do you mean 0.1 + 0.2 != 0.3 ?")

It comforts me (a bit) to think that our robot overlords will inherit all this complexity (by virtue of being created by us), and it might make their revolt slightly less effective.


Nothing wrong with this. Operating in one timezone only simplifies things, no need to overcomplicate it.

Obvious potential failure case: Japanese citizens who are travelling abroad.

Many Japanese studying abroad use VPNs in order to circumvent services blocking overseas traffic.

Most businesses do not care, because it's harder to counter bots when allowing overseas traffic. Moreover, the number of Japanese who leave the country are a small fraction. For reference, the percentage of Japanese who hold a passport is 17%. A small fraction of the 17% represent students studying abroad (or other long-term stays).

There's enough internal demand that people travelling abroad (or studying abroad) are considered edge cases worth ignoring. As surreal as this sounds, this is the current situation.


Using a VPN doesn't change your time zone

Following this logic, python 3 needn’t have been created.

sqlc's approach has its limitations. Its SQLite query parser is generated from an ANTLR grammar, and I've encountered situations where valid SQLite syntax was rejected by sqlc due to their parser failing.

Type inference was okay, since SQLite barely has any types. The bigger issue I had was dealing with migration files. The nice part about SQLx is that `cargo sqlx database setup` will run all necessary migrations, and no special tooling is necessary to manage migration files. sqlc, on the other hand, hard codes support for specific Go migration tools; each of the supported tools were either too opinionated for my use case or seemed unmaintained. SQLx has built-in tooling for migrations; it requires zero extra dependencies and satisfies my needs. Additionally, inferring types inside the actual database has its benefits: (1) no situations where subsets of valid query syntax are rejected, and (2) the DB may be used for actual schema validation.

For an example of why (2) may be better than sqlc's approach: databases like SQLite sometimes allow NULL primary keys; this gets reflected in SQLx when it validates inferred types against actual database schemas. When I last used sqlc, this potential footgun was never represented in the generated types. In SQLx, this footgun is documented in the type system whenever it can detect that SQLite allows silly things (like NULL primary keys when the PK satisfies certain conditions).


I believe sqlc can also connect to the database for type inference now too, fwiw.

Started using Zed about a year ago and, besides Magit, it has managed to completely replace Emacs for me. I was missing a good debugger for a long time, but that also went GA a month ago or so.

One thing that goes underappreciated is the input latency and how light on resources the editor is overall. Whenever I switch tabs to a web browser (or any web app), I can feel the lag in typing now, despite the fact I use an M3 Max MacBook Pro. Zed's built-in terminal used to feel high-latency too, but they recently shipped a bunch of performance improvements, and it's just amazing how clunky inputs feel in web apps feel after using Zed for a long time.

Two things I find interesting about this development.

1. This is a long-standing feature request ever since Zed added any AI capability. Adding AI-related functionality at all was a very controversial move at the time. See https://news.ycombinator.com/item?id=41302782

2. Text threads in Zed came out only 11 months ago. At the time, it felt revolutionary being able to effortlessly paste terminal output and entire folders into context. Additionally, being able to stop the LLM, correct part of its output, and have it continue code generation. Around 4 months ago, agentic coding released, and now this once-revolutionary workflow feels quite primitive. In the meantime, Zed also added screensharing, Linux support for collaboration, a Git UI, a debugger, and performance improvements to the editor.


I use Zed too, and as a longtime magit devotee, I've really been enjoying using gitu [0] in Zed. gitu doesn't have everything magit does, but there's not much I find myself missing.

I have it nicely integrated with Zed by defining the following task, which you can then add a keybinding for if you want:

    {
        "label": "gitu",
        "command": "gitu",
        "reveal_target": "center",
        "hide": "always",
        "env": {
          "VISUAL": "zed",
          "GIT_EDITOR": "vim"
        }
    }

[0] https://github.com/altsem/gitu


Not currently using Zed but low input latency is one of the things that’s kept me on Sublime Text over the years. Might give Zed a shot and see how it stacks up.

The other two editors I use a lot are Xcode and Android Studio, and while the first is usually fine, Android Studio (IntelliJ) feels servicable but a touch sluggish in comparison. Given the popularity of JetBrains IDEs I’m a bit surprised that there’s not more demand from customers to make it more responsive.


JetBrains IDEs with their zero-latency typing mode are actually some of the most responsive editors for typing performance: https://pavelfatin.com/typing-with-pleasure/

They can definitely feel a bit sluggish navigating around once you have a giant codebase and you’re using a lot of features, but code editing has been very responsive for me.


Low latency isn't working well on Linux for zer


Agree . Zed has the worst input latency of any app I've used on Linux.


I took Zed for a test drive about a year ago, and absolutely loved how butter smooth it felt to use. It's impossible to describe to others... you must experience it to understand what you're missing.

However, at least at the time, Zed's extension/plugin ecosystem prevented me from making the jump off vscode. Just like it took me a long while to ween myself off JetBrains and their workflow/plugins, it'll take a long while to do the same here - that's if an equivalent plugin exists (yet).

It seems to me, it would be a killer feature for new IDE's to just embrace vscode's extensions and make them "just work". It would remove a lot of the barriers people have with switching IDE's.

Maybe that's an impossible ask... I have no idea, but it would be pretty sweet.


how does it compare to neovim in terms of input lag?


I have not tried neovim, admittedly. Compared to Eclipse, IntelliJ, and now VScode, it's a completely different animal in terms of fluidity and smoothness.

Usually when I've attempted to explain it to others, people counter with things like "but my IDE already feels smooth", etc. I thought so too, until I tried Zed.

My only "complaint" was/is(?) the plugin ecosystem. It's a fairly new editor, so things might change in time.


Which platform? Input and UIlag is worse than vscode In Linux. Vscode is quite smooth for me


I'm on Fedora (kinoite). It was vastly better than vscode - especially when opening and scrolling large files, etc.

What about other Emacs functions such as org-mode? Or did you never use them to begin with?


These were the comments I came looking for. What existing users of Zed migrated from. I'm pretty curious to try it, but lacking some time to tool yak-shave at the moment.


Why was adding AI integration controversial? You go to their website and it seems the AI integration is the whole point.


You go to https://code.visualstudio.com and it will appear that AI integration is the whole point too. How a thing is currently marketed != How people have been using it.

The original motivation of CSS was the cascading aspect of it. During its development, the web was largely seen as a web of documents. Style sheets tried mimicking how traditional publishers style their documents, books, etc. There is also a need to reconcile document styling applied by the user agent, the site author, and possibly other sources. This is where cascading comes into play.

Problems start to occur when using a system designed around traditional publishing to declare the layout of a web application. This is why CSS eventually gained layout-related functionality (flex, grid, container queries, etc.), among other features.

Tailwind provides two things out-of-the-box that make it convenient for building web applications: (1) it comes with a ready-to-use style system; (2) it allows styles to be colocated with markup. The second point is mostly useful for building UI components. Everything strictly related to presentation can be stored within a single file.

Before using Tailwind, I was a strong advocate of CSS modules (and I still strongly advocate for CSS modules if one wants to avoid Tailwind). With either approach, one can achieve isolated styling between components. Repeated styling or markup is a strong indicator that you should extract something into a component.


> Full Browser window size cancas ... and draw everything yourself

This was already tried to some extent with Flash. Many sites were a single Flash module (or multiple). Implementing accessibility then required maintaining an invisible DOM representation of the Flash canvas. I personally don't want to return to the era where login and register buttons were dedicated Flash modules (MegaUpload did this back in ~2009) and many sites were an 800x600 image using the <area> tag for hitpoints.

Even Flutter has a DOM target because painting to a full <canvas> violates at the very least the operating system's text rendering, scrolling, and accessibility.


I agree with you that these things are important. However they are ( in implementation terms ) rather small issues, and determining i rather pay 4-8x more in development and maintenance costs just for this, while you can go the other way and make those features a bit hardware to implement seems like a not so good business case to me.

e.g we are using Avalonia. Of course everything is drawn in a scalable way, with responsive design etc... Accessibility is built in of course ( with integration with the relevant browser apis ) screenreaders work perfectly as do other accessibility features ) Its not the hap hazard way that flash did this (before there were relevant standards for these features anyways ) Invoking a brower api / interop is easy, the difference is we do not need to compromise our productivity for small things.


Flutter sadly deprecated the DOM renderer: https://docs.flutter.dev/platform-integration/web/renderers


I am not very experienced in async Rust, but it seems there are some pieces of async Rust that rely too much on tokio internals, so using an alternative runtime (like pollster) results in broken code.

Searching for comments mentioning "pollster" and "tokio" on HN brings a few results, but not one I recall seeing a while ago where someone demonstrated an example of a library (using async Rust) that crashes when not using tokio as the executor.

Related documentation: https://rust-lang.github.io/async-book/08_ecosystem/00_chapt...


There's two details that are important to highlight. tokio is actually 2 components, it's the async scheduler, and it's the IO runtime. Pollster is only a scheduler, and does not offer any IO functionality. You can actually use tokio libraries with pollster, but you need to register the IO runtime (and spawn a thread to manage it) - this is done with Runtime::enter() and it configures the thread local interface so any uses of tokio IO know what runtime to use.

There are ideas to abstract the IO runtime interface into the async machinery (in Rust that's the Context object that schedulers pass into the Future) but so far that hasn't gotten anywhere.


Yep. The old async wars in the Rust ecosystem. It's the AsyncRead and AsyncWrite traits. Tokio has its own, there was a standard brewing at the same time in the futures crate. Tokio did their own thing, people burnt out, these traits were never standardized to std.

So you cannot use most of the async crates easily outside Tokio.


thanks, got it!


> Having an extensible format and electing never to extend it seems pointless.

This proves OP analogy regarding USB-C. Having PNG as some generic container for lossless bitmap compression means fragmentation in libraries, hardware support, etc. The reason being that if the container starts to support too many formats, implementations will start restricting to only the subsets the implementers care about.

For instance, almost nobody fully implements MPEG-4 Part 3; the standard includes dozens of distinct codecs. Most software only targets a few profiles of AAC (specifically, the LC and HE profiles), and MPEG-1 Layer 3 audio. Next to no software bothers with e.g. ALS, TwinVQ, or anything else in the specification. Even libavcodec, if I recall correctly, does not implement encoders for MPEG-4 Part 3 formats like TwinVQ. GP's fear is exactly this -- that PNG ends up as a standard too large to fully implement and people have to manually check which subsets are implemented (or used at all).


But where the analogy with USB-C is very good is that just like USB-C, there is no way for a user to tell from the look of the port or the file extension what the capabilities are. Which even for a fairly tech savvy user like me is frustrating. I have a bunch of cables, some purchased years ago, how do I know what is fit for what?

And now think of the younger generation that has grown up with smartphones and have been trained to not even know what a file is. I remember this story about senior high school students failing their school tests during covid because the school software didn't support heif files and they were changing the file extension to jpg to attempt to convert them.

I have no trust the software ecosystem will adapt. For instance the standard libraries of the .net framework are fossilised in the world of multimedia as of 2008-ish. Don't believe heif is even supported to this day. So that's a whole bunch of code which, unless the developers create workarounds, will never support a newer png format.


> there is no way for a user to tell from the look of the port or the file extension what the capabilities are

But that's typical for file extensions. Consider EXE – it is probably an executable, but an executable for what? Most commonly Windows – but which Windows version will this EXE run on? Maybe this EXE only works on Windows 11, and you are still running Windows 10. Or maybe you are running x86-64 Windows, but this EXE is actually for ARM or MIPS or Alpha. Or maybe it is for some other platform which uses that extension for executable files – such as DOS, OS/2, 16-bit Windows, Windows CE, OpenVMS, TOPS-10, TOPS-20, RSX-11...

.html, .js, .css – suggest to use a web browser, but don't tell you whether they'll work with any particular one. Maybe they use the latest features but you use an old web browser which doesn't support them. Maybe they require deprecated proprietary extensions and so only work on some really old browser. Maybe this HTML page only works on Internet Explorer. Maybe instead of UTF-8 it is in some obscure legacy character set which your browser doesn't support.

.zip – supports extensible compression and encryption methods, your unzip utility might not support the methods used to compress/encrypt this particular zip file. This is actually normal for very old ZIP files (from the 1980s) – early versions of PKZIP used various deprecated compression mechanisms, which few contemporary unzip utilities support. The format was extended to 64-bit without changing the extension, there's still a lot of 32-bit only implementations out there. ZIP also supports platform-specific file attributes–e.g. PKZIP for z/OS creates ZIP files which contain metadata about mainframe data storage formats, unzip on another platform is going to have no idea what it means, but the metadata is actually essential to interpreting the data correctly (e.g. if RECFM=V you need to parse the RDWs, if RECFM=F there won't be any)

.xml - okay, it is XML – but that tells you nothing about the actual schema. Maybe you were expecting this xml file to contain historical stock prices, but instead it is DocBook XML containing product documentation, and your market data viewer app chokes on it. Or maybe it really is historical stock prices, but you are using an old version of the app which doesn't support the new schema, so you can't view it. Or maybe someone generated it on a mainframe, but due to a misconfiguration the file came out in EBCDIC instead of ASCII, and your app doesn't know how to read EBCDIC, yet the mainframe version of the same app reads it fine...

.doc - people assume it is legacy (pre-XML) Microsoft Word: every version of which changed the file format, old versions can't read files created with newer versions correctly or at all, conversely recent versions have dropped support for files created in older versions, e.g. current Office versions can't read DOC files created with Word for DOS any more... but back in the 1980s a lot of people used that extension for plain text files which contained documentation. And it was also used by incompatible proprietary word processors (e.g. IBM DisplayWrite) and also desktop publishing packages (e.g. FrameMaker, Interleaf)

.xmi – I've seen this extension used for both XML Model Interchange (XML-based standard for exchanging UML diagrams) and XMIT (IBM mainframe file archive format). Because extensions aren't guaranteed to be unique, many incompatible file formats share the same extension

.com - is it an MS-DOS program, or is it DCL (Digital Command Language)?

.pic - probably some obscure image format, but there are dozens of possibilities

.img – could be either a disk image or a visual image, either way dozens of incompatible formats which use that extension

.db – nowadays most likely SQLite, but a number of completely incompatible database engines have also used this extension. And even if it is SQLite, maybe your version of SQLite is too old to read this file because it uses some features only found in newer versions. And even if SQLite can read it, maybe it has the wrong schema for your app, or maybe a newer version of the same schema which your old version that app doesn't support, or an old version of the schema which the current version of the app has dropped support for...


Just last week I had again some PDFs Okular could not open because of some more uncommon form features.


> Consider EXE – it is probably an executable, but an executable for what? Most commonly Windows

Has anyone ever used .exe for anything other than Windows?


Prior to Windows 95, the vast majority of PC games were MS-DOS exe files – so anyone who played any of those games (whether back in their heyday, or more recently through DOSBox) has run an MS-DOS exe. Most people who ever used Lotus 1-2-3 or WordPerfect were running an MS-DOS exe. Both products were eventually ported to Windows, but were far less popular under Windows than under DOS.

Under Windows 95/98/Me, most command line tools were MS-DOS executables. Their support for 32-bit Windows console apps was very poor, to the extent that the input and output of such apps was proxied through a 16-bit MS-DOS executable, conagent.exe

First time in my life I ever used GNU Emacs, it was an OS/2 exe. That's also true for bash, ls, cat, gcc, man, less, etc... EMX was my gateway drug to Slackware


> Has anyone ever used .exe for anything other than Windows?

Did you know that Microsoft Windows originally ran on top of the much older MS-DOS, which used EXE files as one of its two executable formats? Most Windows users had lots and lots of EXE files which were not Windows executables, but instead DOS executables. And then came Windows 95, which introduced 32-bit Windows executables, but kept the same file extension as 16-bit Windows executables and 16-bit DOS executables.


Way back when, my prof was using his Linux machine to demonstrate how to use GCC. He called the end result .exe but that might have been for the benefit of the Windows users in the room. (Though Linux users being considerate to Windows users, or vice versa, is admittedly a rarity)


JPEG is no different. Only the decoder is specified. As long as the decoder decodes what you give it to the image you wanted to see, you can implement anything. This is how imgoptim/squash/aerate/dietJPG works. By (ab)using this flexibility.

Same is also true for the most advanced codecs. MPEG-* family and MP3 comes to my mind.

Nothing stops PNG from defining a "set of decoders", and let implementers loose on that spec to develop encoders which generate valid files. Then developers can go to town with their creativity.


Video files aren't a good analogy. Before God placed VLC and ffmpeg on earth, you had to install a galaxy of codecs on your computer to get a chance to read a video file and you could never tell exactly what codec was stored in a container, nor if you had the right codec version. Unfortunately there is no vlc and ffmpeg for images (I mean there is, the likes of imagemagick, but the vast majority of software doesn't use them).


I lived through that era (first K-Lite Codec Pack, then CCCP came along), but still it holds.

Proprietary or open, any visual codec is a battleground. Even in commercial settings, I vaguely remember people saying they prefer the end result of one encoder over another, for the same video/image format, not unlike how photographers judge cameras by their colors.

So maybe, this flexibility to PNG will enable or encourage people to write better or at least unorthodox encoders which can be decoded by standard compliant ones.


I honestly don't see an issue with the mpeg-4 example.

Regarding the potential for fragmentation of the png ecosystem the alternative is a new file format which has all the same support issues. Every time you author something you make a choice between legacy support and using new features.

From a developer perspective, adding support for a new compression type is likely to be much easier than implementing logic for an entirely new format. It's also less surface area for bugs. In terms of libraries, support added to a dependency propagates to all consumers with zero additional effort. Meanwhile adding a new library for a new format is linear effort with respect to the number of programs.


I never once in 25 years encountered an issue with an mp4 Container that could Not be solved by installing either the divx or xvid codec. And I extensively used mp4's metatdat for music, even with esoteric Tags.

Not Sure what youre talking abouz.


He's saying that in 25 years, you used only the LC and HE profiles, and didn't encounter TwinVQ even once. I looked at my thousand-odd MPEG-4 files. They're overwhelmingly AAC LC, a little bit of AAC LC SBR, no TwinVQ at all.

If you want to check yours: mediainfo **/*.mp4 | grep -A 2 '^Audio' | grep Format | sort | uniq -c

https://en.wikipedia.org/wiki/TwinVQ#TwinVQ_in_MPEG-4 tells the story of TwinVQ in MPEG-4.


Nothing has been able to replace Magit for me, yet. Having a Zed UI for Git like Magit is my dream feature request.

With that said, Zed has effectively replaced all of Emacs for me, besides Magit. Additionally, typing in other editors feels noticeably higher latency than Zed :)

I've been daily driving Zed for almost a year now -- works best on TypeScript, Rust, and Go projects, in my opinion.

There's just so much functionality Zed has to build out to compete with modern editors (agentic coding, collaboration, debugging, edit prediction, task runners, version control). With that said, for pair-programming sessions with friends, Zed has been perfect since Linux gained screenshare support. However, there's a noticeable "pause in development" for collaboration in order to implement major features like Agentic Coding, and smaller-but-essential features like direnv integration, IME support (typing Japanese in the terminal used to be a clunky, error-prone task), dealing with the endless permutations of Python tooling so that Python files aren't a sea of red lines, etc.


Zed reminds of the days when Atom was big.

It was a good time, but it always left me wondering how long it would last as it leaned heavily on community support for nearly everything useful outside a few packages

Such a situation makes me worry about it keeping up if popularity wanes. With JetBrains for example at least I know that paying for the editor it will keep getting proper updates and support even if it isn’t the most popular (though it is quite popular currently)


Leaning on community support seems ideal because it means you've built a powerful plugin API and people can implement features.

As opposed to having a weak plugin API where all progress depends on the tiny internal team.

The latter suffers more than the first if popularity wanes.

In Atom's case, its lunch was eaten by VSCode which was similar but just better. Based on web tech but with better performance and just as powerful of a plugin API. It wasn't the fact that they let people implement plugins that killed Atom, and they would have been in an even worse situation had they not had a good plugin API.


And Sublime, BBEdit, TextMate, Notepad++, Ultraedit, Slick, vi, vim, XEmacs, Emacs, nano, joe, jEdit,....


They all lag or have lagged on supporting modern features. Even Sublime was slow to adopt LSPs and I believe it’s still a bit complicated to get it working correctly and reliably


You can pay for Zed too. I am.


Paying for zed isn’t the same as paying for extensions to be well maintained. They’re not all in house


Ahh, I see what you mean now and yeah, I agree.


> Most shells take less space than that!

Most shells dynamically link to a runtime your OS provides "for free". The 4.3 MiB binary in question is bundling the Rust runtime and its dependencies.

For reference, a statically-compiled C++ "Hello, World" is 2.2 MiB after stripping.

  % cat hello.nix
  {
    pkgs ? import <nixpkgs> { crossSystem = "aarch64-linux"; }
  }:
  
  pkgs.stdenv.mkDerivation {
    name = "hello-static";
    src = pkgs.writeText "hello.cpp" ''
      #include <iostream>
      int main() {
        std::cout << "Hello, World!" << std::endl;
        return 0;
      }
    '';
    dontUnpack = true;
    buildInputs = [ pkgs.glibc.static ];
    buildPhase = "$CXX -std=c++17 -static -o hello $src";
    installPhase = "mkdir -p $out/bin; cp hello $out/bin/";
  }
  
  % nix-build hello.nix
  ...
  
  % wc -c result/bin/hello
  2224640 result/bin/hello


2.2MiB for "Hello, World"? I must be getting old...

The executable takes 33KB in C, 75KB in nim.


By switching to e.g. musl, you can go down to a single megabyte ;)

But in all seriousness, my example is quite cherrypicked, since nobody will actually statically link glibc. And even if they did, one can make use of link-time optimization to remove lots of patches of unused code. Note that this is the same strategy one would employ to debloat their Rust binaries. (Use LTO, don't aggressively inline code, etc.)


Just a `puts("Hello world!")` with -Os statically linked to musl is 22k


Just for fun, I wondered how small a canonical hello world program could be in macOS running an ARM processor. Below is based on what I found here[0] with minor command-line switch alterations to account for a newer OS version.

ARM64 assembly program (hw.s):

  //
  // Assembler program to print "Hello World!"
  // to stdout.
  //
  // X0-X2 - parameters to linux function services
  // X16 - linux function number
  //
  .global _start             // Provide program starting address to linker
  .align 2

  // Setup the parameters to print hello world
  // and then call Linux to do it.

  _start: mov X0, #1     // 1 = StdOut
          adr X1, helloworld // string to print
          mov X2, #13     // length of our string
          mov X16, #4     // MacOS write system call
          svc 0     // Call linux to output the string

  // Setup the parameters to exit the program
  // and then call Linux to do it.

          mov     X0, #0      // Use 0 return code
          mov     X16, #1     // Service command code 1 terminates this program
          svc     0           // Call MacOS to terminate the program

  helloworld:      .ascii  "Hello World!\n"

Assembling and linking commands:

  as -o hw.o hw.s &&
  ld -macos_version_min 14.0.0 -o hw hw.o -lSystem -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk -e _start -arch arm64

Resulting file sizes:

  -rwxr-xr-x  1 <uid>  <gid>    16K Jun 18 21:23 hw
  -rw-r--r--  1 <uid>  <gid>   440B Jun 18 21:23 hw.o
  -rw-r--r--  1 <uid>  <gid>   862B Jun 18 21:21 hw.s
0 - https://smist08.wordpress.com/2021/01/08/apple-m1-assembly-l...


> The executable takes 33KB in C, 75KB in nim.

Did you statically link Glibc...? Or is this with a non-GNU libc?

Either way, it probably is true that this 2.2MiB number would be smaller on, say, Debian 5. And much smaller on PDP Unix.


We just have large standard libraries now


lto will remove most of it.


> Most shells dynamically link to a runtime your OS provides "for free"

Rust binaries also dynamically link to and rely on this runtime.


That's not intrinsically or pervasively true, although it's not uncommon.


Yes. The main areas where Rust code uses dynamic linking by default are Glibc and OpenSSL (through the popular `native-tls` crate). Most things outside of that will be statically linked by default. There is room to improve the situation by making more C wrapper libraries support either method.

For Rust code talking to Rust libraries (such as `std`), it's a totally different challenge, similar to what you face in C++. You can compile your pure Rust app in a way to divide it up into DLLs. The problem is that the polymorphism in Rust means these DLLs must all be built together. Calling a polymorphic function means code has to be generated for it. The only way around this is for your Rust library to speak the C ABI, which bloats code as building your C-style API surface will resolve all the polymorphized functions/structs, but at least gets you swappable dynamic linking.


The only way to avoid it is to be on linux with no_std, use musl statically or be on embedded. You cannot (or at least are supposed to not be able to) link to glibc statically and on every other OS you can only call syscalls via the system libraries. (Well, technically you can on most systems, it's just not stable across updates. OpenBSD will actively block it though)


Unless the majority of Rust builds on Linux statically link musl libc or use no_std, then it's pervasively true. And it's true on most non-Linux targets, including the BSDs and macOS. It's the same situation with Go.


why did you embed the c++ code in the .nix file?

just to have everything in one file? how to show how to do it with nix?

because it seem simpler to have a separate C++ file, and a simple shell script or makefile to compile it.

e.g. although I could figure out roughly what the .nix file does, many more people would know plain unix shell than nix.

and where is $out defined in the .nix file?


The nix file is besides the point - it gives you a totally hermetic build environment. Not OP, but it’s the only way I know how to get gcc to use a static glibc. All you should pay attention to is that it’s using a static glibc.

$out is a magic variable in nix that means the output of the derivation - the directory that nix moves to its final destination


> Not OP, but it’s the only way I know how to get gcc to use a static glibc.

    /tmp$ gcc -O3 test.c -o test
    /tmp$ ldd test
     linux-vdso.so.1 (0x00007f3d9fbfe000)
     libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3d9f9e8000)
     /lib64/ld-linux-x86-64.so.2 (0x00007f3d9fc00000)
    /tmp$ gcc -static -O3 test.c -o test
    /tmp$ ldd test
     not a dynamic executable


>> Not OP, but it’s the only way I know how to get gcc to use a static glibc.

> /tmp$ gcc -static -O3 test.c -o test /tmp$ ldd test not a dynamic executable

yes, that last line above means it's a statically linked executable.

yes, i had a doubt about what the GP said, about their nix way being the only way to create a statically linked executable.

but I didn't remember all the details, because it's been a while since I worked with C in depth (moved to Java, Ruby, Python, etc.)(though I did a lot of that earlier, even in pre-Linux years), so I didn't say anything else. thanks, Josh Triplett for clarifying.

but one thing I do remember, is that static linking was the only option in the beginning, at least on Unix, and dynamic linking came only some time later.

when I started working on UNIX and C, there was no dynamic linking at all, IIRC.

https://en.m.wikipedia.org/wiki/Static_library

("dynamic linking" topic in above page links to the below page in Wikipedia: )

https://en.m.wikipedia.org/wiki/Dynamic_linker


I thought glibc had some hacks in it to prevent it from working fully when statically linked? Is this just a myth or outdated or only affects C/C++ or what?


The issue is that some features of glibc want to dlopen additional libraries, most notably NSS. If you call `gethostbyname`, even a static glibc will try to dlopen NSS libraries based on /etc/nsswitch.conf, and if the dynamic NSS libraries are incompatible with your statically linked glibc, you'll have problems.

musl, by contrast, doesn't support NSS at all, only /etc/hosts and DNS servers listed in /etc/resolv.conf, so whether you statically or dynamically link musl, you just won't have any support for (for instance) mDNS, or dynamic users, or local containers, or various other bits of name resolution users may expect to Just Work.


thanks.


and by the way, ignorant mindlessly downvoting dudes who don't even bother to check if a comment is right or not, can shove it up, and take a flying leap into Lake Titicaca. they'll meet a lot of their brothers there, like giant frogs.

from a Google search:

>Overview Lake Titicaca, straddling the border between Peru and Bolivia in the Andes Mountains, is one of South America's largest lakes and the world’s highest navigable body of water. Said to be the birthplace of the Incas, it’s home to numerous ruins. Its waters are famously still and brightly reflective. Around it is Titicaca National Reserve, sheltering rare aquatic wildlife such as giant frogs.

:)


The README is likely AI-generated. The actual go.mod file lists 1.23.1 as the Go version[1], which implies a requirement of Go 1.23.1 or higher[2].

[1] https://github.com/piyushgupta53/go-torrent-client/blob/6130...

[2] https://go.dev/doc/modules/gomod-ref#go-notes


Go 1.21 includes an internal auto-updater that can compile later Go versions by looking in the go mod file itself.

So the README is correct.


Automatic toolchain switching does not trigger compilation of later Go toolchains; it only attempts fetching a blob (if available) and using that to perform builds[1]. If a system supports Go 1.21 but not 1.23.1 (e.g. Windows 7, mentioned in a sibling comment), then this project will fail to build on said system. Likewise, if a user on Go 1.21 has disabled automatic toolchain switching, the Go CLI refuses to build the project at all.

Overall, I would say the minimum required Go version is indeed the Go version declared in the go.mod, since that is the declared minimum required toolchain.

[1] https://go.dev/doc/toolchain#select


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: