Hello World, должно быть, самая часто создаваемая компьютерная программа. Уже десятилетия это первая программа, которую пишут люди, когда начинают изучение нового языка программирования.
Конечно же эта простая программа не должна иметь баги?
![](https://habrastorage.org/getpro/habr/upload_files/965/e12/ab9/965e12ab9625e70ae60089545d6f20cc.png)
В конце концов, hello world программы делают только одну вещь. Как там может быть баг?
Hello world в C
Есть множество различных способов написать hello world в C. Версия Википедии, hello world в книге K&R и даже самая старая из известных hello world программ из 1974.
![](https://habrastorage.org/getpro/habr/upload_files/39f/ecd/b8c/39fecdb8c1b493832916796b6bb6ca79.jpg)
Вот ещё одна написанная на "ANSI C":
/* Hello World in C, Ansi-style */
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
puts("Hello World!");
return EXIT_SUCCESS;
}
Это самая надёжная версия из представленных. Она использует (void)
чтобы гарантировать что main
вызывается без аргументов. Она использует EXIT_SUCCESS
макрос вместо предположения что платформа использует 0 для индикации успеха, что не обязательно согласно стандарту C, но мы не рискуем здесь. Она использует соответствующие заголовки, чтобы избежать неявного объявления puts
. Эта версия пытается сделать правильно всё.
И всё равно, тут есть баг.
Во всех версиях представленных выше есть баг.
Баг?
В Linux есть интересный файл под названием "/dev/full", который похож на его более известного собрата "/dev/null", но когда вы пишете в "/dev/full", вместо того чтобы игнорировать данные, он выдаёт ошибку. Он ведёт себя как файл в файловой системе у которой закончилось место.
$ echo "Hello World!" > /dev/full
bash: echo: write error: No space left on device
$ echo $?
1
Это прекрасный инструмент для тестирования того, что программа правильно обрабатывает ошибки ввода-вывода. Неудобно создавать реальную файловую систему без свободного места или эмулировать диск, который сломан, но очень просто направить вывод программы в "/dev/full" и посмотреть что произойдёт.
Так давайте проверим ту программу на C выше:
$ gcc hello.c -o hello
$ ./hello > /dev/full
$ echo $?
0
В отличие от echo
в предыдущем примере, программа ничего не вывела и код возврата был 0. Это значит что программа hello
сообщила что завершилась успешно. Однако, это не так. Мы можем убедиться, что ошибка была, с помощью strace:
$ strace -etrace=write ./hello > /dev/full
write(1, "Hello World!\n", 13) = -1 ENOSPC (No space left on device)
+++ exited with 0 +++
Вот и ошибка о нехватке места, но программа просто проигнорировала её и возвратила 0 - код успеха. Это баг!
Насколько серьёзный это баг? Возможно, hello world - это не самая критичная программа. Однако, hello world делает то же, что и другие программы в реальном мире: печать в стандартный вывод, который может быть перенаправлен в файл, а ведь в реальном мире для файла может закончиться место. Если программа игнорирует такую ошибку и не сообщает о ней через код возврата, её родительский процесс не будет знать что она завершилась с ошибкой и продолжит работать как ни в чём не бывало, хотя вывод, который он ожидал получить, был потерян.
Например, рассмотрим программу, которая печатает yaml файл в стандартный поток вывода. Если у стандартного вывода закончится место, то вывод может быть обрезан в какой-то произвольной точке, хотя это всё ещё может быть валидный yaml. Поэтому нам стоит ожидать, что программы обнаруживают такую ситуацию и сообщают о ней.
Что насчёт других языков?
Ранее мы рассмотрели bash и C, но что насчёт Python, который говорит нам что "Ошибки никогда не должны замалчиваться"? Вот Python 2:
$ python2 hello.py > /dev/full
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr
$ echo $?
0
Он напечатал сообщение в stderr, хотя оно и сбивает с толку. Однако он также вернул 0, что означает что программа завершилась успешно.
К счастью, Python 3 правильно сообщает об ошибке и печатает понятное сообщение о ней:
$ python3 hello.py > /dev/full
Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
OSError: [Errno 28] No space left on device
$ echo $?
120
Вот результаты работы hello world программ на различных языках программирования с различных обучающих сайтов, которые я попробовал запустить:
Язык | Есть ли баг | Тестируемая версия |
C | Да | (все) |
C++ | Да | (все) |
Python 2 | Да | Python 2.7.18 |
Ruby | Да | ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux-gnu] |
Java | Да | openjdk 11.0.11 2021-04-20 |
Node.js | Да | v12.21.0 |
Haskell | Да | The Glorious Glasgow Haskell Compilation System, version 8.8.4 |
Rust | Нет | rustc 1.59.0 (9d1b2106e 2022-02-23) |
Python 3 | Нет | Python 3.9.5 |
Perl | Нет | perl 5, version 32, subversion 1 (v5.32.1) built for x86_64-linux-gnu-thread-multi (with 46 registered patches...) |
Perl 6 | Нет | v2020.12 |
Bash | Нет | GNU bash, version 5.1.4(1)-release (x86_64-pc-linux-gnu) |
Awk | Нет | GNU Awk 5.1.0, API: 3.0 (GNU MPFR 4.1.0, GNU MP 6.2.1) |
OCaml | Нет | 4.08.1 |
Tcl | Нет | 8.6.11 |
C# | Нет | Mono JIT compiler version 6.8.0.105 |
Более полный и актуальный список находится тут.