История изменений
Исправление zx_gamer, (текущая версия) :
Ну только посмеяться можно.
Хелло ворлд на Расте под Linux x86_64 целиком
#![feature(no_core)]
#![feature(lang_items)]
#![no_core]
#![no_main]
#![feature(naked_functions)]
#![feature(decl_macro)]
#![feature(rustc_attrs)]
#![feature(intrinsics)]
// Нужно компилятору
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}
impl Copy for i64 {} // Говорим компилятору, что объект этого типа можно копировать байт за байтом
impl Copy for usize {}
#[rustc_builtin_macro]
pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) {
/* compiler built-in */
}
extern "rust-intrinsic" {
// Чтобы компилятор знал, что есть некоторый код, которого не достичь.
// Например, весь код после exit()
pub fn unreachable() -> !;
}
#[no_mangle]
#[naked]
unsafe extern "C" fn _start() {
// Стырено из книги А.В. Столярова.
// А, простите, там код под 32 бита, в книге 2021 года.
// Значит, не стырено.
asm!(
"mov rdi, [rsp]", // argc
"mov rax, rsp",
"add rax, 8",
"mov rsi, rax", // argv
"call _start_main",
options(noreturn),
)
}
#[no_mangle]
extern "C" fn _start_main(argc: usize, argv: *const *const u8) -> ! {
let status = main(argc, argv);
exit(status);
}
#[no_mangle]
fn main(_argc: usize, _argv: *const *const u8) -> i64 {
let string = b"Hello, world!\n" as *const _ as *const u8;
write(1, string, 14);
return 0;
}
#[inline(never)]
#[no_mangle]
// ! - это never type, компилятор понимает, что функция никогда не возвращается
fn exit(status: i64) -> ! {
unsafe {
syscall1(60, status);
unreachable()
}
}
#[inline(never)]
#[no_mangle]
fn write(fd: i64, data: *const u8, len: i64) -> i64 {
unsafe { syscall3(1, fd, data as i64, len) }
}
#[inline(always)]
unsafe fn syscall1(n: i64, a1: i64) -> i64 {
let ret: i64;
asm!(
"syscall",
in("rax") n,
in("rdi") a1,
lateout("rax") ret,
);
ret
}
#[inline(always)]
unsafe fn syscall3(n: i64, a1: i64, a2: i64, a3: i64) -> i64 {
let ret: i64;
asm!(
"syscall",
in("rax") n,
in("rdi") a1,
in("rsi") a2,
in("rdx") a3,
lateout("rax") ret,
);
ret
}
Заметьте, код с ассемблерными вставками. Смешно, что ПРОСТО на ассемблере код будет в разы короче (а также меньше и быстрее).
format ELF executable 3
entry start
segment readable executable
start:
mov rax, 1 ; write(
mov rdi, 1 ; STDOUT_FILENO,
mov rsi, msg ; "Hello, world!\n",
mov rdx, msglen ; sizeof("Hello, world!\n")
syscall ; );
mov rax, 60 ; exit(
mov rdi, 0 ; EXIT_SUCCESS
syscall ; );
segment readable writeable
msg db "Hello, world!", 10
msglen: $ - msg
При этом код на Rust, естественно, содержит unsafe.
А чтобы написать аналогичный код на Си (без libc) нужно буквально несколько строк:
#include <unistd.h>
main ()
{
write(1, "hello\n", 6);
}
И вы на полном серьезе считаете, что в Rust не нулевой runtime? Чтобы его получить, нужно написать буквально больше, чем на голом асме. В Си можно писать с нулевым runtime достаточно легко. Оверхед очевиден с погрешностью до оптимизатора компилятора.
Исходная версия zx_gamer, :
Ну только посмеяться можно.
Хелло ворлд на Расте под Linux x86_64 целиком
#![feature(no_core)]
#![feature(lang_items)]
#![no_core]
#![no_main]
#![feature(naked_functions)]
#![feature(decl_macro)]
#![feature(rustc_attrs)]
#![feature(intrinsics)]
// Нужно компилятору
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}
impl Copy for i64 {} // Говорим компилятору, что объект этого типа можно копировать байт за байтом
impl Copy for usize {}
#[rustc_builtin_macro]
pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) {
/* compiler built-in */
}
extern "rust-intrinsic" {
// Чтобы компилятор знал, что есть некоторый код, которого не достичь.
// Например, весь код после exit()
pub fn unreachable() -> !;
}
#[no_mangle]
#[naked]
unsafe extern "C" fn _start() {
// Стырено из книги А.В. Столярова.
// А, простите, там код под 32 бита, в книге 2021 года.
// Значит, не стырено.
asm!(
"mov rdi, [rsp]", // argc
"mov rax, rsp",
"add rax, 8",
"mov rsi, rax", // argv
"call _start_main",
options(noreturn),
)
}
#[no_mangle]
extern "C" fn _start_main(argc: usize, argv: *const *const u8) -> ! {
let status = main(argc, argv);
exit(status);
}
#[no_mangle]
fn main(_argc: usize, _argv: *const *const u8) -> i64 {
let string = b"Hello, world!\n" as *const _ as *const u8;
write(1, string, 14);
return 0;
}
#[inline(never)]
#[no_mangle]
// ! - это never type, компилятор понимает, что функция никогда не возвращается
fn exit(status: i64) -> ! {
unsafe {
syscall1(60, status);
unreachable()
}
}
#[inline(never)]
#[no_mangle]
fn write(fd: i64, data: *const u8, len: i64) -> i64 {
unsafe { syscall3(1, fd, data as i64, len) }
}
#[inline(always)]
unsafe fn syscall1(n: i64, a1: i64) -> i64 {
let ret: i64;
asm!(
"syscall",
in("rax") n,
in("rdi") a1,
lateout("rax") ret,
);
ret
}
#[inline(always)]
unsafe fn syscall3(n: i64, a1: i64, a2: i64, a3: i64) -> i64 {
let ret: i64;
asm!(
"syscall",
in("rax") n,
in("rdi") a1,
in("rsi") a2,
in("rdx") a3,
lateout("rax") ret,
);
ret
}
Заметьте, код с ассемблерными вставками. Смешно, что ПРОСТО на ассемблере код будет в разы короче (а также меньше и быстрее).
format ELF executable 3
entry start
segment readable executable
start:
mov rax, 1 ; write(
mov rdi, 1 ; STDOUT_FILENO,
mov rsi, msg ; "Hello, world!\n",
mov rdx, msglen ; sizeof("Hello, world!\n")
syscall ; );
mov rax, 60 ; exit(
mov rdi, 0 ; EXIT_SUCCESS
syscall ; );
segment readable writeable
msg: db "Hello, world!", 10
msglen: equ $ - msg
При этом код на Rust, естественно, содержит unsafe.
А чтобы написать аналогичный код на Си (без libc) нужно буквально несколько строк:
#include <unistd.h>
main ()
{
write(1, "hello\n", 6);
}
И вы на полном серьезе считаете, что в Rust не нулевой runtime? Чтобы его получить, нужно написать буквально больше, чем на голом асме. В Си можно писать с нулевым runtime достаточно легко. Оверхед очевиден с погрешностью до оптимизатора компилятора.