В предыдущих сериях:
Медленно, но верно Раст проникает не только в умы сотрудников больших корпораций, но и в умы школьников и студентов. В этот раз мы поговорим о статье от студента МГУ: https://rustmustdie.com/.
Её репостнул Андрей Викторович Столяров, доцент кафедры алгоритмических языков факультета ВМК МГУ им. М. В. Ломоносова и по совместительству научрук студента-автора статьи.
Я бы сказал, что тут дело даже не в том, что он "неинтуитивный". Дело скорее в том, что компилятор раста сам решает, когда владение "должно" (с его, компилятора, точки зрения) перейти от одного игрока к другому. А решать это вообще-то должен программист, а не компилятор. Ну и начинается пляска вида "как заставить тупой компайлер сделать то, чего я хочу".
Бред это всё.
— А. В. Столяров
Сама статья короткая, но постулирует довольно большой список спорных утверждений, а именно:
- Стандартная библиотека неотделима от языка
- У него отсутствует нулевой рантайм
- В Rust встроен сборщик мусора
- Компилятор генерирует медленный машинный код
На самом деле набросов еще больше, но достаточно и этого списка.
К сожалению, для опровержения этих пунктов мне придется писать максимально уродские хэлло ворлды, которые только можно представить.
Содержание
Опускаемся на самый низ
Нулевой рантайм в Си
Честно говоря, до прочтения статьи я ни разу не встречал такого определения как zero runtime. Немного погуглив, я наткнулся на книгу А. В. Столярова ISBN 978-5-317-06575-7 Программирование: введение в профессию. II: Системы и сети, изданной в 2021 году. В главе "§4.12: (*) Программа на Си без стандартной библиотеки" приводится определение нулевого рантайма и пример программы.
Реализация подпрограммы _start
(под Linux i386):
global _start ; no_libc/start.asm
extern main
section .text
_start:
mov ecx, [esp] ; argc in ecx
mov eax, esp
add eax, 4 ; argv in eax
push eax
push ecx
call main
add esp, 8 ; clean the stack
mov ebx, eax ; now call _exit
mov eax, 1
int 80h
Модуль с "обертками" для системных вызовов:
global sys_read ; no_libc/calls.asm
global sys_write
global sys_errno
section .text
generic_syscall_3:
push ebp
mov ebp, esp
push ebx
mov ebx, [ebp+8]
mov ecx, [ebp+12]
mov edx, [ebp+16]
int 80h
mov edx, eax
and edx, 0fffff000h
cmp edx, 0fffff000h
jnz .okay
mov [sys_errno], eax
mov eax, -1
.okay:
pop ebx
mov esp, ebp
pop ebp
ret
sys_read:
mov eax, 3
jmp generic_syscall_3
sys_write:
mov eax, 4
jmp generic_syscall_3
section .bss
sys_errno resd 1
Простенькая программа, которая принимает ровно один параметр командной строки, рассматривает его как имя и здоровается с человеком, чьё имя указано, фразой Hello, dear NNN (имя подставляется вместо NNN):
/* no_libc/greet3.c */
int sys_write(int fd, const void *buf, int size);
static const char dunno[] = "I don't know how to greet you\n";
static const char hello[] = "Hello, dear ";
static int string_length(const char *s)
{
int i = 0;
while(s[i])
i++;
return i;
}
int main(int argc, char **argv)
{
if(argc < 2) {
sys_write(1, dunno, sizeof(dunno)-1);
return 1;
}
sys_write(1, hello, sizeof(hello)-1);
sys_write(1, argv[1], string_length(argv[1]));
sys_write(1, "\n", 1);
return 0;
}
И сама сборка:
nasm -f elf start.asm
nasm -f elf calls.asm
gcc -m32 -Wall -c greet3.c
ld -melf_i386 start.o calls.o greet3.o -o greet3
На машине автора этих строк (Столярова) размер файла составил 816 байт. На моей машине 13472 байта.
Что ж, применим clang-14
, ld.lld-14
, -Os
и strip
; и на моей машине получилось 1132 байта:
nasm -f elf start.asm
nasm -f elf calls.asm
clang-14 -m32 -Os -Wall -c greet3.c
ld.lld-14 -melf_i386 start.o calls.o greet3.o -o greet3
strip ./greet3
В своей книге Столяров делает очень сильное утверждение, а именно:
Но дело даже не в этой экономии (размера исполняемого файла — Прим. авт.)…
Намного важнее сам принцип: язык Си позволяет полностью отказаться от возможностей стандартной библиотеки. Кроме Си, таким свойством — абсолютной независимостью от библиотечного кода, также иногда называемым zero runtime — обладают на сегодняшний день только языки ассемблеров; ни один язык высокого уровня не предоставляет такой возможности.
Что ж, давайте разберемся, обладает ли Раст таким свойством.
Из чего состоит хэлло ворлд
Рассмотрим базовый пример, приведённый на официальном сайте языка Раст:
fn main() {
println!("Hello, world!");
}
Так как println!
— это макрос, а не функция, у нас есть возможность посмотреть на код после раскрытия макроса. Для этого воспользуемся утилитой cargo-expand:
#![feature(prelude_import)]
#[prelude_import]
use std::prelude::rust_2021::*;
#[macro_use]
extern crate std;
fn main() {
{
::std::io::_print(::core::fmt::Arguments::new_v1(
&["Hello, world!\n"],
&match () {
_args => [],
},
));
};
}
Компилятор вставил импорт стандартной библиотеки extern crate std;
и прелюдию use std::prelude::rust_2021::*;
. Именно эти неявные вставки я и хотел показать.
Стандартная библиотека — это удобный набор функций, коллекций, структур и типажей в окружении, когда у тебя есть ос, фс, куча, сокеты и прочая хипстота. Считается, что 93.9% программистам именно такое поведение (автоматическое включение std и прелюдии) и требуется.
Весь API стандартной библиотеки подробно описан в официальной документации. Есть удобный быстропоиск: https://std.rs/QUERY
, где QUERY
— ваш запрос, например https://std.rs/mutex.
Отключаем std
Тем не менее, для остальных 19% программистов предусмотрен режим отключения стандартной библиотеки с помощью атрибута #![no_std]
.
#![no_std]
#![feature(start, lang_items)]
// Говорим компилятору влинковать libc
#[cfg(target_os = "linux")]
#[link(name = "c")]
extern "C" {
// Объявляем внешнюю функцию из libc
fn puts(s: *const u8) -> i32;
}
#[start] // Говорим, что выполнение надо начинать с этого символа
fn main(_argc: isize, _argv: *const *const u8) -> isize {
unsafe {
// В Расте строки не нуль-терминированные
puts("Hello, world!\0".as_ptr());
}
return 0;
}
#[panic_handler] // Удовлетворяем компилятор
fn panic(_panic: &core::panic::PanicInfo<'_>) -> ! {
loop {}
}
#[lang = "eh_personality"] // Удовлетворяем компилятор
extern "C" fn eh_personality() {}
Так как здесь и далее код требует нестабильных фич, советую воткнуть в корень проекта файлик, который будет управлять версией компилятора:
$ cat rust-toolchain.toml
[toolchain]
channel = "nightly-2022-06-09"
Если такой версии компилятора на компе нет, то cargo
вызовет rustup
, чтобы тот поставил нужную версию. Если такой компилятор есть, то любые действия с cargo
по компиляции будут использовать указанную в конфиге версию.
А в Cargo.toml
добавить отключение размотки, все равно она в коде нигде не будет использоваться:
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
Для этого хэлло ворлда cargo-expand
покажет следующее:
#[prelude_import]
use core::prelude::rust_2021::*;
#[macro_use]
extern crate core;
...
То есть компилятор неявно вставил импорт библиотеки core (extern crate core;
) и прелюдию core (use core::prelude::rust_2021::*;
).
Ниже представлена сводная таблица, описывающая разницу между core
и std
.
Функциональность | core |
std |
---|---|---|
динамическое выделение памяти | нет *1 | да |
коллекции (Vec , HashMap и т.д.) |
нет *2 | да |
доступ к std
|
нет | да |
доступ к core
|
да | да |
низкоуровневая разработка | да | нет |
- да, если используется крейт
alloc
и настроен глобальный аллокатор; - да, если коллекции тоже
#![no_std]
и зависят отcore
.
Большинство структур и типажей стандартной библиотеки описываются именно в core
, а не в std
:
- Методы примитивов
bool
,i32
...; - Типы
Range
,Option
,Result
,Cell
,RefCell
,PhantomData
...; - Типажи
Hash
,Drop
,Debug
,Iterator
,Future
,Unpin
...; - Функции
forget
,drop
,swap
...
Отключаем core
Мы не ищем лёгких путей, поэтому мы отключим и std
, и core
с помощью атрибута #![no_core]
. Такая функциональность по разным оценкам требуется от 3577 до 4518 людям в мире на момент написания статьи (именно столько людей контрибутят в компилятор Раста, но github даёт одни цифры, а git log --format="%an" | sort -u | wc -l
другие). Вы же не думаете, что я тут беру статистику с потолка?
#![feature(no_core)]
#![feature(lang_items)]
#![no_core]
// Говорим компилятору влинковать libc
#[cfg(target_os = "linux")]
#[link(name = "c")]
extern {}
// Функция `main` на самом деле не точка входа, а вот `start` - да.
#[lang = "start"]
fn start<T>(_main: fn() -> T, _argc: isize, _argv: *const *const u8) -> isize {
42
}
// Втыкаем символ, чтобы не получить ошибку undefined reference to `main'
fn main() { }
// Нужно компилятору
#[lang = "sized"]
pub trait Sized {}
Проверить работоспособность можно только по коду возврата: echo $?
должен вернуть 42.
Мы почти добрались до самого низа. У нас нет возможности складывать числа, если попробовать их сложить, будет ошибка:
error[E0369]: cannot add `{integer}` to `{integer}`
--> src/main.rs:14:8
|
14 | 40 + 2
| -- ^ - {integer}
| |
| {integer}
Да ничего у нас нет, только определение примитивов i8
, usize
, str
, но работать с ними нельзя.
Отключаем crt
Rust компилирует объектные файлы самостоятельно, но использует внешний (обычно это системный) линковщик. По умолчанию линковщик добавляет *crt*.o
, в которых определяется стартовый символ (_start
), но этот символ можно переопределить. Для этого отключаем сишный рантайм:
$ cargo rustc -- -C link-args=-nostartfiles
Или с помощью конфига в корне проекта можно задать флаги линковки:
$ cat .cargo/config
[build]
rustflags = ["-C", "link-args=-nostartfiles"]
Тогда с .cargo/config
и rust-toolchain.toml
файлом сборка проекта осуществляется короткой командой cargo build
. Ну или вы можете вбивать cargo +nightly-2022-06-09 rustc -- -C link-args=-nostartfiles
.
Вид нашего хэлло ворлда приобретает форму:
#![feature(no_core)]
#![feature(lang_items)]
#![no_core]
#![no_main]
#[no_mangle]
extern "C" fn _start() {}
// Нужно компилятору
#[lang = "sized"]
pub trait Sized {}
Девственный ассемблер:
$ objdump -Cd ./target/debug/hello_world
./target/debug/hello_world: file format elf64-x86-64
Disassembly of section .text:
0000000000001000 <_start>:
1000: c3 retq
Компилируем и запускаем:
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.00s
Running `target/debug/hello_world`
Illegal instruction (core dumped)
Прекрасно. С этим можно начинать работать.
Пишем хэлло ворлд
Вообще до мейна происходит очень много интересного: инициализация статиков, профилировщика. Советую посмотреть доклад Мэтта Годболта:
https://www.youtube.com/watch?v=dOfucXtyEsU
Мы же напишем простой _start
с прыжком в _start_main
, который и будет вызывать функцию main
. Подложка в виде _start_main
нужна, чтобы можно было положиться на компилятор в вопросах передачи аргументов и очистки стека.
Символ _start
Его мы будем писать на ассемблере. В std
/core
препроцессор ассемблерных вставок включается по умолчанию, а вот нам надо включить его явно.
#![feature(decl_macro)]
#![feature(rustc_attrs)]
#[rustc_builtin_macro]
pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) {
/* compiler built-in */
}
_start
— это специальная функция, которой не требуется пролог и эпилог, поэтому ее надо пометить как naked
.
#![feature(naked_functions)]
#[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) -> isize {
main(argc, argv);
0
}
#[no_mangle]
fn main(_argc: usize, _argv: *const *const u8) -> isize {
// И вот мы добрались до мейна
return 0;
}
Компилируем и запускаем: Illegal instruction (core dumped)
. Я чую, что мы на правильном пути!
Сисколы
Всего нам понадобится два сискола: exit
и write
.
"Подложки" для сисколлов я хочу реализовать в общем виде, чтобы они принимали номер сисколла и аргументы (syscall1
— 1 аргумент, syscall3
— 3 аргумента).
man 2 syscall
дает нам следующую информацию:
Every architecture has its own way of invoking and passing arguments
to the kernel. The details for various architectures are listed
in the two tables below.
The first table lists the instruction used to transition to kernel mode
(which might not be the fastest or best way to transition to the kernel,
so you might have to refer to vdso(7)), the register used to indicate
the system call number, the register(s) used to return the system call
result, and the register used to signal an error.
Arch/ABI Instruction System Ret Ret Error Notes
call # val val2
───────────────────────────────────────────────────────────────────
i386 int $0x80 eax eax edx -
x86-64 syscall rax rax rdx - 5
The second table shows the registers used to pass the system call arguments.
Arch/ABI arg1 arg2 arg3 arg4 arg5 arg6 arg7 Notes
──────────────────────────────────────────────────────────────
i386 ebx ecx edx esi edi ebp -
x86-64 rdi rsi rdx r10 r8 r9 -
Завершение процесса
У данного системного вызова есть замечательное свойство — он никогда не возвращается. Этот факт можно использовать с помощью типов и интринзиков, чтобы дать понять компилятору, что любой код после данного сискола никогда не будет выполнен. Это реализуется через тип !
(never) и интринзик unreachable
:
#![feature(intrinsics)] // подключаем фичу объявления интринзиков
extern "rust-intrinsic" {
// Чтобы компилятор знал, что есть некоторый код, которого не достичь.
// Например, весь код после exit()
pub fn unreachable() -> !;
}
#[no_mangle]
extern "C" fn _start_main(argc: usize, argv: *const *const u8) -> ! {
let status = main(argc, argv);
exit(status);
}
#[inline(never)]
#[no_mangle]
// ! - это never type, компилятор понимает, что функция никогда не возвращается
fn exit(exit_code: i64) -> ! {
unsafe {
syscall1(60, exit_code);
unreachable()
}
}
#[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
}
Если запустить получившийся бинарник, echo $?
вернет ожидаемый 0.
Запись в файл
Настало время реализовать вывод "Hello, world!" в стандартный поток вывода! \<Не забыть изменить на менее глупую фразу перед публикацией>.
#[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]
fn write(fd: i64, data: *const u8, len: i64) -> i64 {
unsafe { syscall3(1, fd, data as i64, len) }
}
#[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
}
#![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
}
Запускаем и проверяем:
$ cargo r
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/hello_world`
Hello, world!
$ echo $?
0
$ strip ./target/debug/hello_world
$ stat -c %s ./target/debug/hello_world
13096
Оно работает! Но размер бинарника 13096 байт. Что ж, применим ld.lld-14
:
$ cat .cargo/config
[build]
rustflags = ["-C", "linker=ld.lld-14"]
$ cargo r
Compiling hello_world v0.1.0 (/home/USER/rustmustdie/article/chapter_4)
Finished dev [unoptimized + debuginfo] target(s) in 0.13s
Running `target/debug/hello_world`
Hello, world!
$ echo $?
0
$ strip ./target/debug/hello_world
$ stat -c %s ./target/debug/hello_world
1712
Уии!
То есть нет =( Получилось 1712 байт против 1132 байт сишной реализации. Не забываем, что в сишной реализации вообще другой код, он хитрый, с непростым приветствием, то есть у него больше функциональность, но меньше размер.
Приводим к общему знаменателю
Вот было бы здорово, если бы у нас был:
- Единый компилятор (
gcc
), - Единый линковщик (
ld.lld-14
), - Одни и те же флаги компиляции
-Os -masm=intel -m32 -fno-pic -fno-asynchronous-unwind-tables
, - Одни и те же флаги линковки
--no-pie --no-dynamic-linker
, - Да и код, выполняющий одну и ту же программу, не правда ли?
- Чтобы был
_start
с прыжком в_start_main
, который и будет вызывать функциюmain
, - Чтобы было два сискола
sys_exit
иsys_write
(именование из книги Столярова), - Чтобы они были реализованы через обобщение сисколов
syscall1
иsyscall3
.
Жаль, что все вместе это невозможно… Or is it?
Компилируем gcc и rustc_codegen_gcc
Архитектура компилятора rustc
позволяет подключить не только бекенд llvm
, но и gcc
. Проект, который занимается поддержкой gcc
, называется rustc_codegen_gcc. Конечно же не все так просто, с ним надо провести профекалтическую работу.
$ sudo apt install flex make gawk libgmp-dev libmpfr-dev libmpc-dev gcc-multilib
Клонируем rustc_codegen_gcc
, патченный gcc
и собираем gcc
с поддержкой i386
:
# У меня версия 1724042e228c3 от Wed Sep 14 09:22:50 2022
$ git clone https://github.com/rust-lang/rustc_codegen_gcc.git --depth 1
rustc_codegen_gcc$ cd rustc_codegen_gcc
#BUILD GCC (20 mins)
rustc_codegen_gcc$ git clone https://github.com/antoyo/gcc.git --depth 1
rustc_codegen_gcc$ cd gcc
rustc_codegen_gcc/gcc$ mkdir build install
rustc_codegen_gcc/gcc$ cd build
rustc_codegen_gcc/gcc/build$ ../configure --enable-host-shared --enable-languages=jit,c --disable-bootstrap --enable-multilib --target=x86_64-pc-linux-gnu --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64 --enable-multiarch --prefix=$(pwd)/../install
rustc_codegen_gcc/gcc/build$ make -j8
rustc_codegen_gcc/gcc/build$ make install # в папочку ../install
rustc_codegen_gcc/gcc/build$ cd ../../
rustc_codegen_gcc$ echo $(pwd)/gcc/install/lib/ > gcc_path
Мастер ветка пока что не поддерживает i386
из коробки, но это можно исправить:
diff --git a/config.sh b/config.sh
index b25e215..18574f2 100644
--- a/config.sh
+++ b/config.sh
@@ -20,8 +20,9 @@ else
fi
HOST_TRIPLE=$(rustc -vV | grep host | cut -d: -f2 | tr -d " ")
-TARGET_TRIPLE=$HOST_TRIPLE
+#TARGET_TRIPLE=$HOST_TRIPLE
#TARGET_TRIPLE="m68k-unknown-linux-gnu"
+TARGET_TRIPLE="i686-unknown-linux-gnu"
linker=''
RUN_WRAPPER=''
@@ -33,6 +34,8 @@ if [[ "$HOST_TRIPLE" != "$TARGET_TRIPLE" ]]; then
# We are cross-compiling for aarch64. Use the correct linker and run tests in qemu.
linker='-Clinker=aarch64-linux-gnu-gcc'
RUN_WRAPPER='qemu-aarch64 -L /usr/aarch64-linux-gnu'
+ elif [[ "$TARGET_TRIPLE" == "i686-unknown-linux-gnu" ]]; then
+ : # do nothing
else
echo "Unknown non-native platform"
fi
diff --git a/src/back/write.rs b/src/back/write.rs
index efcf18d..e640fbe 100644
--- a/src/back/write.rs
+++ b/src/back/write.rs
@@ -14,6 +14,8 @@ pub(crate) unsafe fn codegen(cgcx: &CodegenContext<GccCodegenBackend>, _diag_han
let _timer = cgcx.prof.generic_activity_with_arg("LLVM_module_codegen", &*module.name);
{
let context = &module.module_llvm.context;
+ context.add_command_line_option("-m32");
+ context.add_driver_option("-m32");
let module_name = module.name.clone();
let module_name = Some(&module_name[..]);
diff --git a/src/base.rs b/src/base.rs
index 8cc9581..fb8bd88 100644
--- a/src/base.rs
+++ b/src/base.rs
@@ -98,7 +98,7 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol, supports_
context.add_command_line_option("-mpclmul");
context.add_command_line_option("-mfma");
context.add_command_line_option("-mfma4");
- context.add_command_line_option("-m64");
+ context.add_command_line_option("-m32");
context.add_command_line_option("-mbmi");
context.add_command_line_option("-mgfni");
context.add_command_line_option("-mavxvnni");
diff --git a/src/context.rs b/src/context.rs
index 2699559..056352a 100644
--- a/src/context.rs
+++ b/src/context.rs
@@ -161,13 +161,13 @@ impl<'gcc, 'tcx> CodegenCx<'gcc, 'tcx> {
let ulonglong_type = context.new_c_type(CType::ULongLong);
let sizet_type = context.new_c_type(CType::SizeT);
- let isize_type = context.new_c_type(CType::LongLong);
- let usize_type = context.new_c_type(CType::ULongLong);
+ let isize_type = context.new_c_type(CType::Int);
+ let usize_type = context.new_c_type(CType::UInt);
let bool_type = context.new_type::<bool>();
// TODO(antoyo): only have those assertions on x86_64.
- assert_eq!(isize_type.get_size(), i64_type.get_size());
- assert_eq!(usize_type.get_size(), u64_type.get_size());
+ assert_eq!(isize_type.get_size(), i32_type.get_size());
+ assert_eq!(usize_type.get_size(), u32_type.get_size());
let mut functions = FxHashMap::default();
let builtins = [
diff --git a/src/int.rs b/src/int.rs
index 0c5dab0..5fd4925 100644
--- a/src/int.rs
+++ b/src/int.rs
@@ -524,7 +524,7 @@ impl<'a, 'gcc, 'tcx> Builder<'a, 'gcc, 'tcx> {
// when having proper sized integer types.
let param_type = bswap.get_param(0).to_rvalue().get_type();
if param_type != arg_type {
- arg = self.bitcast(arg, param_type);
+ arg = self.cx.context.new_cast(None, arg, param_type);
}
self.cx.context.new_call(None, bswap, &[arg])
}
diff --git a/src/lib.rs b/src/lib.rs
index e43ee5c..8fb5823 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -104,6 +104,7 @@ impl CodegenBackend for GccCodegenBackend {
let temp_dir = TempDir::new().expect("cannot create temporary directory");
let temp_file = temp_dir.into_path().join("result.asm");
let check_context = Context::default();
+ check_context.add_command_line_option("-m32");
check_context.set_print_errors_to_stderr(false);
let _int128_ty = check_context.new_c_type(CType::UInt128t);
// NOTE: we cannot just call compile() as this would require other files than libgccjit.so.
И поверх этого патча надо применить еще один, чтобы libgccjit.so
компилировал только с нужным набором флагов:
diff --git a/src/base.rs b/src/base.rs
index fb8bd88..d5268dc 100644
--- a/src/base.rs
+++ b/src/base.rs
@@ -87,29 +87,11 @@ pub fn compile_codegen_unit<'tcx>(tcx: TyCtxt<'tcx>, cgu_name: Symbol, supports_
// Instantiate monomorphizations without filling out definitions yet...
//let llvm_module = ModuleLlvm::new(tcx, &cgu_name.as_str());
let context = Context::default();
- // TODO(antoyo): only set on x86 platforms.
context.add_command_line_option("-masm=intel");
- // TODO(antoyo): only add the following cli argument if the feature is supported.
- context.add_command_line_option("-msse2");
- context.add_command_line_option("-mavx2");
- // FIXME(antoyo): the following causes an illegal instruction on vmovdqu64 in std_example on my CPU.
- // Only add if the CPU supports it.
- context.add_command_line_option("-msha");
- context.add_command_line_option("-mpclmul");
- context.add_command_line_option("-mfma");
- context.add_command_line_option("-mfma4");
context.add_command_line_option("-m32");
- context.add_command_line_option("-mbmi");
- context.add_command_line_option("-mgfni");
- context.add_command_line_option("-mavxvnni");
- context.add_command_line_option("-mf16c");
- context.add_command_line_option("-maes");
- context.add_command_line_option("-mxsavec");
- context.add_command_line_option("-mbmi2");
- context.add_command_line_option("-mrtm");
- context.add_command_line_option("-mvaes");
- context.add_command_line_option("-mvpclmulqdq");
- context.add_command_line_option("-mavx");
+ context.add_command_line_option("-fno-pic");
+ context.add_command_line_option("-fno-asynchronous-unwind-tables");
+ context.add_command_line_option("-Os");
for arg in &tcx.sess.opts.cg.llvm_args {
context.add_command_line_option(arg);
Клонируем llvm
и собираем rustc_codegen_gcc
:
#BUILD RUSTC: (5 mins)
rustc_codegen_gcc$ git clone https://github.com/llvm/llvm-project llvm --depth 1 --single-branch
rustc_codegen_gcc$ export RUST_COMPILER_RT_ROOT="$PWD/llvm/compiler-rt"
rustc_codegen_gcc$ ./prepare_build.sh # download and patch sysroot src
rustc_codegen_gcc$ ./build.sh
Всё, теперь у нас есть собранный своими ручками компилятор Си (~/rustc_codegen_gcc/gcc/install/bin/gcc
), libgccjit.so
для компиляции Раста c захардкоженными флагами -Os -masm=intel -m32 -fno-pic -fno-asynchronous-unwind-tables
и скрипт ~/rustc_codegen_gcc/cargo.sh
, который подсовывает фронтенду rustc
бекенд gcc
.
Хэлло ворлд на Си под i386
int sys_write(int fd, const void *buf, int size);
void sys_exit(int status);
static int main(int argc, char **argv);
static int syscall1(int n, int a1);
static int syscall3(int n, int a1, int a2, int a3);
static const char hello[] = "Hello, world!\n";
void _Noreturn __attribute__((naked)) _start() {
__asm volatile (
"_start:\n"
" mov ecx, [esp]\n"
" mov eax, esp\n"
" add eax, 4\n"
" push eax\n"
" push ecx\n"
" call _start_main\n"
);
}
void _Noreturn _start_main(int argc, char **argv) {
int status = main(argc, argv);
sys_exit(status);
}
static int main(int argc, char **argv)
{
sys_write(1, hello, sizeof(hello)-1);
return 0;
}
void _Noreturn __attribute__ ((noinline)) sys_exit(int status) {
syscall1(1, status);
__builtin_unreachable();
}
int __attribute__ ((noinline)) sys_write(int fd, const void *buf, int size) {
return syscall3(4, fd, (int) buf, size);
}
static int syscall1(int n, int a1) {
int ret;
__asm volatile (
" int 0x80"
: "=a" (ret)
: "0" (n), "b" (a1)
: "memory"
);
return ret;
}
static int syscall3(int n, int a1, int a2, int a3) {
int ret;
__asm volatile (
" int 0x80"
: "=a" (ret)
: "0" (n), "b" (a1), "c" (a2), "d" (a3)
: "memory"
);
return ret;
}
Все эти приседания с _Noreturn
, static
, __attribute__((naked))
прямое отражение того, что было в коде на Расте. Т.е. говорим компилятору, что из sys_exit
нельзя выйти, static
— для красивого инлайна (и чтобы в итоговом бинаре отсутствовал такой символ), а __attribute__((naked))
— чтобы компилятор не вставил пролог и эпилог для _start
.
Сборка:
~/rustc_codegen_gcc/gcc/install/bin/gcc -Os -masm=intel -m32 -fno-pic -fno-asynchronous-unwind-tables -Wall -Wno-main -c hello_world.c
ld.lld-14 --no-pie --no-dynamic-linker hello_world.o -o hello_world
strip hello_world
objcopy -j.text -j.rodata hello_world
Проверяем:
$ ./build.sh
$ ./hello_world
Hello, world!
Хэлло ворлд на Расте под i386
#![feature(no_core)]
#![feature(lang_items)]
#![feature(naked_functions)]
#![feature(decl_macro)]
#![feature(rustc_attrs)]
#![feature(intrinsics)]
#![no_core]
#![no_main]
#[lang = "sized"]
pub trait Sized {}
#[lang = "copy"]
pub trait Copy {}
impl Copy for i32 {}
impl Copy for usize {}
#[rustc_builtin_macro]
pub macro asm("assembly template", $(operands,)* $(options($(option),*))?) {
/* compiler built-in */
}
extern "rust-intrinsic" {
pub fn unreachable() -> !;
}
#[no_mangle]
#[naked]
unsafe extern "C" fn _start() {
asm!(
"mov ecx, [esp]",
"mov eax, esp",
"add eax, 4",
"push eax",
"push ecx",
"call _start_main",
options(noreturn),
)
}
#[no_mangle]
extern "C" fn _start_main(argc: usize, argv: *const *const u8) -> ! {
let status = main(argc, argv);
sys_exit(status);
}
#[no_mangle]
fn main(_argc: usize, _argv: *const *const u8) -> i32 {
let string = b"Hello, world!\n" as *const _ as *const u8;
sys_write(1, string, 14);
return 0;
}
#[inline(never)]
#[no_mangle]
fn sys_write(fd: i32, data: *const u8, len: i32) -> i32 {
unsafe { syscall3(4, fd, data as _, len) }
}
#[inline(never)]
#[no_mangle]
fn sys_exit(status: i32) -> ! {
unsafe {
syscall1(1, status);
unreachable()
}
}
#[inline(always)]
unsafe extern "C" fn syscall1(n: i32, a1: i32) -> i32 {
let ret: i32;
asm!(
"int 0x80",
in("eax") n,
in("ebx") a1,
lateout("eax") ret,
);
ret
}
#[inline(always)]
unsafe fn syscall3(n: i32, a1: i32, a2: i32, a3: i32) -> i32 {
let ret: i32;
asm!(
"int 0x80",
in("eax") n,
in("ebx") a1,
in("ecx") a2,
in("edx") a3,
lateout("eax") ret,
);
ret
}
Сборка:
# cargo.sh, предоставляемый rustc_codegen_gcc, принимает только переменную окружения CG_RUSTFLAGS
# поэтому в .cargo/config эти переменные не установить. Увы.
export CG_RUSTFLAGS="-C linker=ld.lld-14 -C link-args=--no-pie -C link-args=--no-dynamic-linker"
~/rustc_codegen_gcc/cargo.sh b --target i686-unknown-linux-gnu
strip ./target/i686-unknown-linux-gnu/debug/hello_world
objcopy -j.text -j.rodata ./target/i686-unknown-linux-gnu/debug/hello_world
Проверяем:
$ ./build.sh
rustc_codegen_gcc is build for rustc 1.65.0-nightly (748038961 2022-08-25) but the default rustc version is rustc 1.63.0-nightly (7466d5492 2022-06-08).
Using rustc 1.65.0-nightly (748038961 2022-08-25).
Compiling hello_world v0.1.0 (/home/USER/rustmustdie/article/chapter_6)
Finished dev [unoptimized + debuginfo] target(s) in 0.84s
$ ./target/i686-unknown-linux-gnu/debug/hello_world
Hello, world!
Сравнение
Си | Раст |
---|---|
$ stat -c %s hello_world 496 $ size -A hello_world hello_world : section size addr .rodata 15 4194516 .text 84 4198627 Total 99 |
$ stat -c %s ./hello_world 464 $ size -A ./hello_world ./hello_world : section size addr .rodata 14 4194484 .text 82 4198594 Total 96 |
Вот так, размер файла на Расте получился 464 байта, а на Си — 494 байт. Предлагаю читателю самостоятельно ответить на вопрос, обладает ли Раст свойством абсолютной независимости от библиотечного кода, также иногда называемым zero runtime.
Для интересующихся, вот вся инфа о бинарях:
$ objdump -Cd hello_world
hello_world: file format elf32-i386
Disassembly of section .text:
004010e3 <_start>:
4010e3: 8b 0c 24 mov (%esp),%ecx
4010e6: 89 e0 mov %esp,%eax
4010e8: 83 c0 04 add $0x4,%eax
4010eb: 50 push %eax
4010ec: 51 push %ecx
4010ed: e8 27 00 00 00 call 401119 <_start_main>
4010f2: 0f 0b ud2
004010f4 <sys_exit>:
4010f4: 55 push %ebp
4010f5: b8 01 00 00 00 mov $0x1,%eax
4010fa: 89 e5 mov %esp,%ebp
4010fc: 53 push %ebx
4010fd: 8b 5d 08 mov 0x8(%ebp),%ebx
401100: cd 80 int $0x80
00401102 <sys_write>:
401102: 55 push %ebp
401103: b8 04 00 00 00 mov $0x4,%eax
401108: 89 e5 mov %esp,%ebp
40110a: 53 push %ebx
40110b: 8b 4d 0c mov 0xc(%ebp),%ecx
40110e: 8b 55 10 mov 0x10(%ebp),%edx
401111: 8b 5d 08 mov 0x8(%ebp),%ebx
401114: cd 80 int $0x80
401116: 5b pop %ebx
401117: 5d pop %ebp
401118: c3 ret
00401119 <_start_main>:
401119: 55 push %ebp
40111a: 89 e5 mov %esp,%ebp
40111c: 83 ec 0c sub $0xc,%esp
40111f: 6a 0e push $0xe
401121: 68 d4 00 40 00 push $0x4000d4
401126: 6a 01 push $0x1
401128: e8 d5 ff ff ff call 401102 <sys_write>
40112d: 31 c0 xor %eax,%eax
40112f: 89 04 24 mov %eax,(%esp)
401132: e8 bd ff ff ff call 4010f4 <sys_exit>
$ readelf -a hello_world
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2s complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x4010e3
Start of program headers: 52 (bytes into file)
Start of section headers: 336 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 4
Size of section headers: 40 (bytes)
Number of section headers: 4
Section header string table index: 3
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .rodata PROGBITS 004000d4 0000d4 00000f 00 A 0 0 4
[ 2] .text PROGBITS 004010e3 0000e3 000054 00 AX 0 0 1
[ 3] .shstrtab STRTAB 00000000 000137 000019 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00400034 0x00400034 0x00080 0x00080 R 0x4
LOAD 0x000000 0x00400000 0x00400000 0x000e3 0x000e3 R 0x1000
LOAD 0x0000e3 0x004010e3 0x004010e3 0x00054 0x00054 R E 0x1000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0
Section to Segment mapping:
Segment Sections...
00
01 .rodata
02 .text
03
$ objdump -s ./hello_world
./hello_world: file format elf32-i386
Contents of section .rodata:
4000d4 48656c6c 6f2c2077 6f726c64 210a00 Hello, world!..
Contents of section .text:
4010e3 8b0c2489 e083c004 5051e827 0000000f ..$.....PQ.''....
4010f3 0b55b801 00000089 e5538b5d 08cd8055 .U.......S.]...U
401103 b8040000 0089e553 8b4d0c8b 55108b5d .......S.M..U..]
401113 08cd805b 5dc35589 e583ec0c 6a0e68d4 ...[].U.....j.h.
401123 0040006a 01e8d5ff ffff31c0 890424e8 .@.j......1...$.
401133 bdffffff ....
$ objdump -Cd ./target/i686-unknown-linux-gnu/debug/hello_world
./target/i686-unknown-linux-gnu/debug/hello_world: file format elf32-i386
Disassembly of section .text:
004010c2 <_start>:
4010c2: 8b 0c 24 mov (%esp),%ecx
4010c5: 89 e0 mov %esp,%eax
4010c7: 83 c0 04 add $0x4,%eax
4010ca: 50 push %eax
4010cb: 51 push %ecx
4010cc: e8 25 00 00 00 call 4010f6 <_start_main>
004010d1 <sys_write>:
4010d1: 55 push %ebp
4010d2: b8 04 00 00 00 mov $0x4,%eax
4010d7: 89 e5 mov %esp,%ebp
4010d9: 53 push %ebx
4010da: 8b 5d 08 mov 0x8(%ebp),%ebx
4010dd: 8b 4d 0c mov 0xc(%ebp),%ecx
4010e0: 8b 55 10 mov 0x10(%ebp),%edx
4010e3: cd 80 int $0x80
4010e5: 5b pop %ebx
4010e6: 5d pop %ebp
4010e7: c3 ret
004010e8 <sys_exit>:
4010e8: 55 push %ebp
4010e9: b8 01 00 00 00 mov $0x1,%eax
4010ee: 89 e5 mov %esp,%ebp
4010f0: 53 push %ebx
4010f1: 8b 5d 08 mov 0x8(%ebp),%ebx
4010f4: cd 80 int $0x80
004010f6 <_start_main>:
4010f6: 55 push %ebp
4010f7: 89 e5 mov %esp,%ebp
4010f9: 83 ec 0c sub $0xc,%esp
4010fc: 6a 0e push $0xe
4010fe: 68 b4 00 40 00 push $0x4000b4
401103: 6a 01 push $0x1
401105: e8 c7 ff ff ff call 4010d1 <sys_write>
40110a: 31 c0 xor %eax,%eax
40110c: 89 04 24 mov %eax,(%esp)
40110f: e8 d4 ff ff ff call 4010e8 <sys_exit>
$ readelf -a ./target/i686-unknown-linux-gnu/debug/hello_world
ELF Header:
Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00
Class: ELF32
Data: 2s complement, little endian
Version: 1 (current)
OS/ABI: UNIX - System V
ABI Version: 0
Type: EXEC (Executable file)
Machine: Intel 80386
Version: 0x1
Entry point address: 0x4010c2
Start of program headers: 52 (bytes into file)
Start of section headers: 304 (bytes into file)
Flags: 0x0
Size of this header: 52 (bytes)
Size of program headers: 32 (bytes)
Number of program headers: 4
Size of section headers: 40 (bytes)
Number of section headers: 4
Section header string table index: 3
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .rodata PROGBITS 004000b4 0000b4 00000e 00 A 0 0 4
[ 2] .text PROGBITS 004010c2 0000c2 000052 00 AX 0 0 1
[ 3] .shstrtab STRTAB 00000000 000114 000019 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
p (processor specific)
There are no section groups in this file.
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00400034 0x00400034 0x00080 0x00080 R 0x4
LOAD 0x000000 0x00400000 0x00400000 0x000c2 0x000c2 R 0x1000
LOAD 0x0000c2 0x004010c2 0x004010c2 0x00052 0x00052 R E 0x1000
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0
Section to Segment mapping:
Segment Sections...
00
01 .rodata
02 .text
03
$ objdump -s ./target/i686-unknown-linux-gnu/debug/hello_world
./target/i686-unknown-linux-gnu/debug/hello_world: file format elf32-i386
Contents of section .rodata:
4000b4 48656c6c 6f2c2077 6f726c64 210a Hello, world!.
Contents of section .text:
4010c2 8b0c2489 e083c004 5051e825 00000055 ..$.....PQ.%...U
4010d2 b8040000 0089e553 8b5d088b 4d0c8b55 .......S.]..M..U
4010e2 10cd805b 5dc355b8 01000000 89e5538b ...[].U.......S.
4010f2 5d08cd80 5589e583 ec0c6a0e 68b40040 ]...U.....j.h..@
401102 006a01e8 c7ffffff 31c08904 24e8d4ff .j......1...$...
401112 ffff ..
Сишная версия толще на одну инструкцию ud2
(занимает 2 байта) и на один нуль в конце строки. В sys_exit
аргументы пушатся в разном порядке, а в бинарях в целом символы находятся по разным адресам, а так бинари абсолютно идентичны.
Полученный результат стал возможен благодаря автору rustc_codegen_gcc
— Antoyo. Он ведет блог, в котором периодически репортит о прогрессе данного проекта. И прогресс действительно поражает воображение. Пользуясь моментом, я прошу вас запатреонить Antoyo или проспонсировать его на гитхабе. Он делает важное дело не только для языка Раст, но и для проекта gcc
(улучшает libgccjit.so
), что позволит в будущем отвязаться от llvm
и, например, компилировать модули ядра Линукса под все доступные gcc
платформы.
Выводы
Именно это свойство — zero runtime — делает Си единственным и безальтернативным кандидатом на роль языка для реализации ядер операционных систем и прошивок для микроконтроллеров. Тем удивительнее, насколько мало людей в мире этот момент осознают; и стократ удивительнее то, что людей, понимающих это, судя по всему, вообще нет среди членов комитетов по стандартизации (языка Си)…
— А. В. Столяров
Спасибо, буду знать.
Весь смысл статьи не в том, что Раст можно опустить на уровень ассемблера, а в том что он остается по прежнему высокоуровневым языком, средства которого активно используют там, где раньше доминировал Си. Даже без стандартной библиотеки у Раста остаётся система типов, которая позволяет минимизировать количество ошибок, возникающих из-за человеческого фактора. И это одна из главных причин, по которой Раст хотят включить в ядро Линукса.
Вся эта история с доцентом, студентом МГУ и статьёй https://rustmustdie.com/ показывает, что где-то внутри вуза построен странный образовательный процесс, который мешает студентам получать актуальную информацию и формировать независимое мнение.
Я бы мог пройтись практически по всем пунктам студента: толстые бинари, медленный код, сборщик мусора, что времена жизни — это хак компилятора, что заимствования слабее указателей. Но это бы лишний раз показало, что преподаватель, который влил эту чушь в голову студентам МГУ, не просто находится не на своем месте, он вообще ничего не смыслит в системном программировании.
Я бы хотел, чтобы в МГУ (самом МГУ!) ученые и студенты были открыты к познанию. Ведь в этом и есть суть университетов, нет? Слишком многого хочу?..
Ссылки на код
Весь код из примеров, как и патчи, доступен в репозитории на гитхабе. Проверяйте, перепроверяйте.
Если у вас возник вопрос, а как же реализовать сложение чисел в среде без рантайма, вот ссылка на Linux x86_64 проект с минимальной реализацией арифметики, адресной арифметики, базовых операций вроде взятия размера слайса, ссылки на данные толстого указателя и т.д., благодаря чему в хэлло ворлде вычисляется размер строки, а не подставляется магическое число:
#[no_mangle]
fn main() {
print("Hello world!\n");
}
fn print(string: &str) {
unsafe {
write(1, string.as_ptr(), string.len())
};
}
Комментарии (149)
ZaMaZaN4iK
30.09.2022 18:12-80терминально бесполезный мусор.
мне же приходится бороться с мусорной пропагандой
на это ловятся все бездарные тупорылые дошколята, которые нажрались пропаганды и пошли блеять. Т.е. ЦА раста является дошколятский биомусор, который код писать не может
почему ЦА говнораста - это веб-обезьяны и бездарные докшоялта? Почему всё, что пишется - это всякая дристня, которую пишут на жс/дристоне/другой скриптухе?
ещё раз. Какой может быть с тобою "по делу", если ты жертва пропаганды? Ты не можешь существовать вне
в любом случае молодец
Зная вышеперечисленные факты (и многие другие), сложно воспринимать сравнение так называемого языка программирования Rust с чем-то нормальным всеръёз. Для более подробного и глубокого обсуждения рекомендую присоедениться к чату: https://t.me/proriv_zaparti2
indestructable
03.10.2022 11:05+8Удивляет, что у некоторых людей присутствует иррациональная ненависть к Расту. Я так понимаю, в основном это сишники и плюсеры. Казалось бы, не нравится - проходи мимо. Аналогичное явление я наблюдал у линуксоидов, НЕНАВИДЯЩИХ виндоуз.
F0iL
03.10.2022 12:04+1Да ладно, это в обе стороны работает.
Многие плюсовики интересуются Rust'ом и не прочь писать на нем.
А у других нелюбовь к расту появляется из-за его очень назойливого сообщества. Тут на Хабре был отличный комментарий про это:Фанаты раста в тредах про с++ как веганы на барбекю пикнике. Орут громче всех, а кто вас звал то?
QtRoS
30.09.2022 18:17+52Я бы хотел, чтобы в МГУ (самом МГУ!) ученые и студенты были открыты к познанию. Ведь в этом и есть суть университетов, нет? Слишком многого хочу?..
Не слишком, но давайте будем честными: ВУЗ, где преподаватели знают про Rust и даже могут подискутировать о нем, уже довольно хорош по сравнению со средним по больнице. В моем университете меня ругали за лишнюю переменную (память тратится типа) в коде. Или что процессоров по технологии ~32нм не может существовать в природе, потому что это меньше длина волны видимого света. Никто не запрещает подвергать сомнению слова преподавателя, перепроверять информацию, затем возвращаться к нему для обсуждения. В сомнении рождается истина (с)
le2
30.09.2022 18:27+32на ютубе есть лекция Жореса Алферова из которой можно узнать что он сам не знает что такое маркетинговые нанометры, он уверен что это размер транзистора.
Лично я около трех раз ловил на экзамене преподов что они сами не понимают что-то по мелочам. К моему счастью они признавали ошибку. Но это нормально.WASD1
30.09.2022 20:48+2Ну вообще "эффективная плотность упаковки 'неквадратных' элементов" - это как раз самая лучшая метрика, для такого понятия, как размер элемента.
Halt
01.10.2022 07:47+19Я бы сказал есть разница в знании терминов (особенно сиюминутных) и понимании принципов. Человек может не помнить, как называется та или иная хреновина, но понимать теорию, стоящую за ней. Ученому можно объяснить этот термин за 1 минуту, в то же время маркетологу, сыпящему выдуманными терминами и ничерта не понимающему в происходящем, хоть кол на голове теши, больше он понимать не станет, ибо не хватает именно фундаментальных знаний. Так что это не принципиально.
yatanai
01.10.2022 10:52+2Ну он в каком-то смысле прав, для планарных транзисторов меньше условных 24-28нм сделать нельзя. Но вот когда оно в тридэ, то можно сделать из этого 14нм, там же 2 транзистора на ячейку!
le2
30.09.2022 18:23+22Отличие явное. Си без стандартной библиотеки остается языком Си (то что происходит в ядре Линукса/UNIX которые собираются без всех зависимостей, в том числе и без стандартной библиотеки), а что останется от Раста без стандартной библиотеки? Будет ли это Растом?
Ну и полное унижение для большинства программистов - а можете ли вы оперировать микросекундами на своём любимом языке? - это можно на ассемблерах и Си. В прочих языках всё недертминированное во времени придется вырезать и от любого языка останется нечто Си-подобное.
Только не надо передергивать, я не говорю что Си это хорошо всегда и для всего.Halt
30.09.2022 18:46+55а что останется от Раста без стандартной библиотеки? Будет ли это Растом?
Да, будет. Автор статьи отломал только импорты. Сам язык от этого не перестал быть языком. Можно все так же использовать высокоуровневые абстракции, чем активно пользуются разработчики.
Например, эмбеддеры пишут библиотеки, где на уровне типов нельзя выстрелить в ногу (хех) микроконтроллеру с неправильно сконфигурированными портами или переполнить стек. Разработчики операционных систем пишут абстракции конкуренции и доступа к ресурсам, не позволяющим их использовать неверно.
Но они видимо Столярова не посещали и не знают, что это невозможно.
Вообще, кому интересно рекомендую книгу Rust Embedded.
victor_1212
30.09.2022 21:10+2вмк всегда очень не равный состав преподавателей имел, люди которые пришли из промышленности (в те далекие времена, когда приходилось там бывать - Королев и др) это top notch, насколько знаю сильные люди там еще есть, но мало, и не Столяров, хотя против его книг ничего не имею, откровенного вранья там не замечал, просто по серьезному он не программист, а преподаватель скорее, хотя возможно сам думает иначе, так что все что он про rust пишет лучше не читать (imho как обычно)
Stan88
30.09.2022 23:30+5Я с Вами согласен, что это классно, круто, современно...надежно и безопасно. И даже есть разработчики встраиваемых систем, которые пишут на очередном новом языке библиотеки и абстракции. И который ну вот уже наконец-то победит этот архаичный Си.
Хм...проверим статистику что у нас там по RTOS, драйверам и их поддержке:MOST POPULAR RTOS (2021)
Deos (DDC-I)
embOS (SEGGER)
FreeRTOS (Amazon)
Integrity (Green Hills Software)
Keil RTX (ARM)
LynxOS (Lynx Software Technologies)
MQX (Philips NXP / Freescale)
Nucleus (Mentor Graphics)
Neutrino (BlackBerry)
PikeOS (Sysgo)
SafeRTOS (Wittenstein)
ThreadX (Microsoft Express Logic)
µC/OS (Micrium)
VxWorks (Wind River)
Zephyr (Linux Foundation
Ой, какая неожиданность - все они написаны на С/С++ (в большей степени на С). А как же так то...не надежно все это - сколько безногих разработчиков и промышленных систем.
А если серьезно - каждый выбирает инструмент для своей задачи, под свое соотношение затраченного времени к предполагаемой прибыли.
Ну а когда там появится RTOS на Rust, которая ложиться на большее кол-во популярных семейств МК, с паком необходимых драйверов, примерами под доступные dev-борды и хоть каким-то комьюнити - вот тогда можно будет потратить свое время и перейти на этот без сомнения хороший инструмент. Но пока что это экономически не обоснованно.F0iL
30.09.2022 23:51+42пишут на очередном новом языке
Rust за время своего существования уже в принципе доказал, что он не "очередной" :)
Ой, какая неожиданность - все они написаны на С/С++ (в большей степени на С)
Суровое наследие легаси, ну да, чему тут удивляться? Правда, при правильном подходе это не является фатальной проблемой - см. то же ядро Linux, которое уже 30 лет как разрабатывается на Си и у которого все API на Си, а теперь без двух минут как в нем можно писать драйвера на Rust, и Земля не остановилась :)
Stan88
01.10.2022 00:28+5Да я же ничего против не имею) Каждый использует что ему удобно. Просто когда говорят для общей кучи - а вон мол и эмбеддеры даже пишут, значит все можно делать. Да конечно можно, не вопрос вообще.
Вот только бизнесу абсолютно начхать на чем вы там будете реализовывать задачу - его волнуют сроки, суммы и доступный уровень надежности при первых двух параметрах. И эмбеддер с опытом решения промышленных задач всегда отдаст предпочтение готовым либам, прошедшим через 100 тысяч узкоспециализированных разработчиков и экземплам по RTOS от разработчиков систем, а выигранное время потратит на бизнес-логику процесса. А не писать с нуля драйвера для интерфейсов, клиентов, серверов и прочего)Halt
01.10.2022 07:55+18Вы будете таки смеяться, но бизнес очень даже понимает преимущества, которые дает раст при разработке и особенно при поддержке систем. Я говорю не про титанов из FAANG которые уже давно в теме, а про железячников: ядра операционных систем, системы реального времени, авионика, automotive сектор и в скором времени космос. Ferrous systems не зря пилят расширенную формальную спецификацию языка именно для критических систем.
Чтобы понять почему, достаточно ответить на вопрос, почему Торвальдс не показал свой знаменитый средний палец Расту так как делал это C++ все 30 лет.
Используя вашу логику можно сказать что и C++ никому не уперся, ибо серьезный бизнес уже 50 лет как использует COBOL. Но это уже, простите, демагогия.
yatanai
01.10.2022 11:03-5Хах, не понимаю претензий к крестам. Единственное что в нём плохо, что он не может определится для чего он.
На подобии того, как в "низкоуровневое" языке появились СОМ интерфейсы, которые вообще-то крестовые Шарп пародии на самих себя. Как это воспринимать нормально?
wertex
01.10.2022 15:42+3Для Rust есть RTIC для Cortex-M. Довольно таки интересный аналог имеющихся RTOS.
mayorovp
01.10.2022 18:42+2Да, вот ссылка для тех кто не может этот rtic нагуглить: https://rtic.rs/1/book/en/
0xd34df00d
30.09.2022 19:59+36Си без стандартной библиотеки остается языком Си
Нет, потому что стандарт языка С описывает стандартную библиотеку.
(то что происходит в ядре Линукса/UNIX которые собираются без всех зависимостей, в том числе и без стандартной библиотеки),
Тоже нет, потому что там используются фичи, не описанные стандартом С. Иными словами, существует удовлетворяющая стандарту реализация, которая не скомпилирует ядро линукса, или результат будет рабоотать не так, как ожидается.
AnthonyMikh
30.09.2022 20:02+11Иными словами, существует удовлетворяющая стандарту реализация, которая не скомпилирует ядро линукса
Есть, называется clang :^)
thevlad
30.09.2022 23:05+1Нет, уже давно не называется https://www.kernel.org/doc/html/latest/kbuild/llvm.html
AnthonyMikh
01.10.2022 23:29+5При этом поддерживает далеко не все платформы, на которых собирается Linux, и для поддержки сборки которой потребовалось вносить патчи как в само ядро, так и в clang, для поддержки GNU-расширений.
thevlad
01.10.2022 23:40А какие не поддерживаются для которых есть бэкенд?
AnthonyMikh
02.10.2022 00:06+1Написано по ровно той ссылке, которую вы и привели (._. )
thevlad
02.10.2022 00:12Не увидел, там я вижу лишь список поддерживаемых платформ, и они все есть в бэкенде llvm. Соответственно мой вопрос: какие есть платформы из линукса, поддерживаемые бэкендом llvm, но которых нет в этом списке.
thevlad
01.10.2022 00:27Но существующие реализации некоторых компиляторов, собирают ядро на поддерживаевом им наборе архитектур. И тут вспоминается анекдот про математика и воздушный шар.
checkpoint
01.10.2022 12:04+6Только не надо передергивать, я не говорю что Си это хорошо всегда и для всего.
Си - это хорошо всегда и для всего!
mSnus
02.10.2022 02:03+13Если пишешь ты на Си,
Будь хоть трижды ламер -
Каждый скажет о тебе:
"Он крутой программер!"
Halt
30.09.2022 18:36+41Именно это свойство — zero runtime — делает Си единственным и
безальтернативным кандидатом на роль языка для реализации ядер
операционных систем и прошивок для микроконтроллеров.Очень мило. Особенно на фоне того, что буквально на днях у Лины заработал Linux драйвер графики для Apple M1, написанный на расте, а в ядро Linux вот-вот заедет его поддержка, одобренная самим Торвальдсом.
Комментарии Лины по поводу разработки
On the Rust side, I have to say I'm super pleased with my experience writing a driver like this in Rust! I've had zero concurrency issues (and the driver uses fine-grained locking, there's no big driver lock) - once single processes worked, running multiple apps concurrently just worked. Also zero memory leaks, dangling CPU or GPU pointers, use-after frees / free order mistakes, or anything like that! The only memory corruption issues I ran into were either fundamental mistakes in my unsafe DRM abstraction or core GPU memory management code, or happened from the GPU side (there's an issue with TLB invalidation, that's what the ugly workaround is for).
I feel like Rust encourages good driver design and then the compiler goes a long way towards making the resulting code correct. All in all I didn't really have that many bugs to fix, mostly just logic issues (both because I'm new to DRM and because the GPU interface is all reverse engineered and we're still working out the details).
The workaround for the GPU-side TLB inval issue has a large performance hit, but without that, kmscube does run at 1000+ FPS, and that's with a lot of suboptimal components that will be improved over time (e.g. my current allocator allocates/maps/unmaps/frees tons of little GPU structures per frame), so I'm also very optimistic about the performance aspect!
The only major Rust issue I ran into is the lack of placement new, which I ended up working around with a very ugly place!() macro (it still has a soundness issue too, I need to fix it to drop things if initialization fails halfway through). Without that, I was quickly overflowing the kernel stacks (which is particularly ugly to debug without CONFIG VMAP STACK, which I didn't have set at first...). With the macro though, the stack frames are under control enough that there's no issue, but l'd really love to see core language support for this. I think it's really necessary for kernel/embedded development.
Про эмбеддед я вообще молчу…
Politura
30.09.2022 18:48+7Меня терзают смутные сомнения. Мне почему-то кажется, что вы писали не на расте, а на ассемблере, который компилируется растовским компилятором. Но если так, то почему вы это сравниваете с greet3.c из главы "Нулевой рантайм в Си" который написан исключительно на си, без каких-либо ассембленых вставок и миллионов указивок си перестать работать как си?
Mingun
30.09.2022 19:22+22"Нулевой рантайм в Си", написанный на Си, не работает. Чтобы он заработал, ему еще нужно 2 файла, которые не компилируются компилятором Си. Можете и на Расте функции, реализованные целиком в ассемблере, вытащить в отдельные файлы, либо на Си затащить их в файл Си (что и было сделано в конце).
миллионов указивок си перестать работать как си?
Если вы про атрибуты, то это нормальный код на Расте. Ничего не "перестает работать как раст", borrow checker не отключите, не надейтесь :)
Politura
30.09.2022 20:55+1Можете и на Расте функции, реализованные целиком в ассемблере, вытащить в отдельные файлы
Вот как-раз такой вариант был-бы интересен, когда все, что не-раст вынесли в отдельное место, оставив чистый язык. Там чуть выше сообщение о том, что Раст собирается заезжать в ядро Линукса, я более чем уверен, что он будет заезжать именно в таком виде, а не ввиде страшной мешанины из ассемблерных вставок.
либо на Си затащить их в файл Си (что и было сделано в конце).
Этот подход мне и не понравился. Там нет си вообще, там какая-то уродливая и нечитаемая фигня, зачем назвали это си? Сравните с изначальным вариантом, когда вынесли отдельно чистый ассемблер и отдельно - чистый и всем понятный си.
thevlad
30.09.2022 23:20+8Это все исключительно дело вкуса, от того что вы заметете ассемблер под ковер - он никуда не денется.
aelaa
30.09.2022 19:15+24Вроде бы достаточно знать кто такой Столяров, чтобы не пытаться с ним спорить. (Никакого негатива в его сторону, просто у него отличающиеся взгляды на жизнь)
maeris
02.10.2022 02:53+6А кто это вообще?
WASD1
02.10.2022 13:26+4человек, который отключает в своём браузере JS по соображениям "я точно хочу знать что происходит на моей машине".
Во всякмо случае когда-то не очень давно отключал, судя по интервью.
san-smith
02.10.2022 20:15+8Адепт секты истинных программистовЕдинственным прибежищем программистов-любителей внезапно оказалась веб-разработка. К сожалению, начав в этой области, люди обычно ею же и заканчивают. Разницу между скриптами, составляющими веб-сайты, и серьёзными программами можно сравнить, пожалуй, с различием между мопедом и карьерным самосвалом; кроме того, привыкнув к «всепрощающему» стилю скриптовых языков типа того же PHP, большинство неофитов оказывается принципиально неспособно перейти к программированию на строгих языках типа Джавы или тем более Си, а хитросплетения Си++ для таких людей оказываются за горизонтом понимания. Веб-кодеры, как правило, получают неплохие деньги, не подозревая при этом, что такое настоящее программирование и что они для себя потеряли.
© А.В. Столяров, Программирование: введение в профессию Стр. 15.
и борцун с международными террористамиЗа несколько лет, прошедших с момента предыдущего издания, мир несколько изменился: группа международных террористов, по недоразумению называющихся комитетом по стандартизации Си++, развернула весьма бурную и эффективную деятельность по окончательному уничтожению языка. Вышедшие последовательно «стандарты» С++11, С++14 и, наконец, С++17 не переставали удивлять публику: каждый раз казалось, что более мрачного и безумного извращения придумать уже нельзя, и каждый раз выход очередного «стандарта» наглядно демонстрировал, что всё возможно. Если под «языком Си++» понимать С++17, то о применении такого инструмента на практике не может быть никакой речи, т.е. с языком Си++ следует попращаться, устроить торжественные похороны и поискать альтернативу; впрочем, то же самое можно сказать про все его «стандарты», начиная с самого первого, принятого в 1998 году; строго говоря, язык Си++ как уникальное явление был уничтожен именно тогда.
© А.В. Столяров, «Введение в язык СИ++», стр. 7
Gorthauer87
30.09.2022 19:29+2Зачем спорить с царём?
AnthonyMikh
30.09.2022 19:50+4Это не Царь. Царь анонимен, а Столяров — нет и до прямых оскорблений оппонентов вроде не опускается.
0xd34df00d
30.09.2022 19:55+36Напомнило, как в 2008-м году на каком-то конкурсе для 11-классников какой-то чувак тоже из МГУ доказывал мне, что единственный способ написать быстрый программный комплекс — писать все на ассемблере. Видимо, к 2022-му году МГУшники
смогли осилитьподумали, что смогли осилить хотя бы С. Возможно, к 2050-му смогут и в раст.Я бы хотел, чтобы в МГУ (самом МГУ!)
А что сам МГУ? Громкое имя, а на деле — пшик.
omxela
01.10.2022 14:36+2единственный способ написать быстрый программный комплекс — писать все на ассемблере
Это Ваш пересказ слов чувака. Смысл такого утверждения туманен. Что такое "быстрый"? В сравнении с чем? Быстрота нужна для чего? И что такое загадочный "программный комплекс"? Я могу представить себе задачи, когда нужно считать каждую микросекунду (для чего нужен сторонний счётчик, кстати) и контролировать каждый поток буквально по тактам. В этом случае ассемблер - самый простой выбор для человека, которому нужен результат, а не понты и имитация понимания.
Громкое имя, а на деле — пшик
Подобное огульное утверждение вряд ли имеет смысл. Каждый "пшик" имеет имя и фамилию.
0xd34df00d
02.10.2022 02:56+16Что такое "быстрый"? В сравнении с чем? Быстрота нужна для чего? И что такое загадочный "программный комплекс"?
Расскажу историю чуть подробнее. В 2008-м году я был в 11-м классе и участвовал во всяких разных конкурсах, потому что они давали дополнительные ништяки при поступлении. Участвовал я с одним своим тогдашним проектом — софтиной на C++ с кутями, которая тогда фокусировалась на скачивании и обмене файлами через всякие HTTP, FTP, торренты и прочее, ну и там ещё были всякие вспомогательные фичи вокруг этой темы — вроде RSS (чтобы из лент автоматом торренты доставать, например), массовой загрузки по шаблону, и так далее. Ну и одна из основных фишек была в модульности, но неважно.
Один из таких конкурсов, то ли при Бауманке, то ли при МИФИ, то ли при вообще какой-то компании (в духе microsoft imagine cup), не помню уже, имел стендовый формат — школьники стояли с плакатами, к ним подходили преподы/другие школьники/интересующиеся родители/етц и разговаривали. На моём плакате (ну как плакате, наборе листочков A4) было, среди прочего, сравнение моего поделия по CPU и RAM с прочими гуёвыми софтинами с аналогичной функциональностью — помню, azureus был (потому что его тормоза на моей тогдашней 512-мегабайтной машине вдохновили меня писать что-то своё), было что-то ещё.
Охренеть, я даже фотку нашёл.Поручики, не ржать над прогрессивными технологиями программирования. Я был молод и глуп
и не знал о хаскеле.Ну и хотя в среднем всем на моё поделие было, к счастью, плевать, ко мне таки подошла делегация из какого-то товарища из МГУ в летах и нескольких, возможно, аспирантов вокруг него, посмотрела на мой плакат, похмыкала, и товарищ в летах принялся доказывать, что это всё ерунда, софт на C++ заведомо не может быть нетребовательным к ресурсам (что бы это ни значило), и что единственный способ — писать на ассемблере. Да, не прошивку для контроллера, не кусок ядра ОС, а… да, кроссплатформенный торрент-клиент, например.
На диалог товарищ настроен не был, несколько раз в разных формах повторил одну и ту же мысль, подытожил «нет, эффективный код возможен только на ас-сем-бле-ре!», и пошёл хмыкать над другими плакатами.
В принципе, ровно в тот момент я окончательно убедился в правильности своего решения не тратить время на поступление в МГУ, будь то Мехмат или ВМК, так что чуваку, в принципе, спасибо.
Я могу представить себе задачи, когда нужно считать каждую микросекунду (для чего нужен сторонний счётчик, кстати) и контролировать каждый поток буквально по тактам.
На современном железе? Успехов с контролем по тактам на современных процессорах с OoO, кэшами (включая микроинструкции, TLB и вот это всё), и так далее. На многозадачной и нериалтаймовой ОС — тем более успехов.
В этом случае ассемблер — самый простой выбор для человека, которому нужен результат
Это, мягко скажем, зависит. Если логика задачи простая, портировать не нужно, и так далее — да, возможно. Но иначе может оказаться выгоднее и проще писать на плюсах/расте/етц, или вообще наваять свой DSL.
а не понты и имитация понимания.
Эта формулировка подразумевает неявное противопоставление каким-то другим инструментам, которые дают понты либо имитацию понимания. Можете уточнить, какие инструменты вы имели в виду?
Подобное огульное утверждение вряд ли имеет смысл. Каждый "пшик" имеет имя и фамилию.
Каждый «заборостроительный» тоже имеет имя и фамилию, но непонятно, что из этого следует.
sshikov
02.10.2022 13:10+3>когда нужно считать каждую микросекунду
Я тоже могу представить себе задачи, когда речь идет о сотнях терабайтов данных в сутки, например. И никто микросекунды не считает, потому что цель программного комплекса, например, подготовить отчет для регулятора сегодня «к закрытию банковского дня». И это тоже программный комплекс, и тоже быстрый — но в своем понимании. И даже мысли писать что-то на C++ никому в голову не приходит, прошу заметить, не говоря уже про ассемблер.
>Смысл такого утверждения туманен.
Вот именно по этой причине. Чувак, который делает такие утверждения без уточнения, что такое быстрый и так далее — балабол. Я не согласен с вашим выводом про ассемблер, несколько по другой причине, нежели озвучил 0xd34df00d, а скорее по той, что очень редко требования по быстродействию относятся ко всей большой системе, И даже если вы пишете что-то на ассемблере, уже очень давно имеет смысл писать только самые критичные части, и только в тех случаях, когда ваш компилятор не умеет скажем использовать все возможности процессора. Все же остальное зачастую можно написать на том, на чем быстрее и дешевле. Потому что «нужен результат» как правило почему-то не когда попало, а вчера.
RC_Cat
30.09.2022 20:20+9для опровержения этих пунктов мне придется писать максимально уродские хэлло ворлды, которые только можно представить
Погодите, и вы вправду не считаете что в этой фразе что-то нет так?
Pastoral
30.09.2022 21:06+11Я вроде как понимаю содержание этого замечательного цикла статей, но определённо и полностью не понимаю смысл.
Сравнение языков по быстродействию которое мне интересно - написать сколь-либо нетривиальный код наиболее естественным способом без всяких заморочек с оптимизацией и сравнить, потому что именно это и будет происходить в реальной жизни. Подобные же экзерсисы отдают стремлением к формальной доказательности, что в свою очередь отдаёт стремлением к (пере)убедительности.
Отсюда вопрос: а что, обнаружилось что в некоторых кругах организована травля Rust? Если да, то чем она может быть вызвана и есть ли координация с Гугол ринувшимся делать Carbon чисто случайно как только Rust начал набирать популярность?
0xd34df00d
30.09.2022 21:19+11Отсюда вопрос: а что, обнаружилось что в некоторых кругах организована травля Rust? Если да, то чем она может быть вызвана и есть ли координация с Гугол ринувшимся делать Carbon чисто случайно как только Rust начал набирать популярность?
Ломающие новости! Выявлено спонсирование opennet'а гуглом!
WASD1
30.09.2022 22:55Подобные же экзерсисы отдают стремлением к формальной доказательности,
что в свою очередь отдаёт стремлением к (пере)убедительности.Почему наши российские учебники (книги этим тоже грешат, но меньше) по IT такие плохие - во всяком случае в худшую сторону отличаются от иностранных?
Потому, что первый фильтр происходит по формальным критериям (в том числе корректности написанных утверждений: есть методика испытаний, есть 0.99999% производительности - так и пиши) и при их невыполнении девочка-секретарка (в лучшем случае аспиратнтка) напишет "не пущать".
А второй фильтр, состоящий из содержательных, тот самый "нетривиальный код написанный естественным образом" - фиг докажешь и представишь как плюс.victor_1212
01.10.2022 00:41> Почему наши российские учебники (книги этим тоже грешат, но меньше) по IT такие плохие
в первую очередь интересны оригинальные книги написанные по собственному опыту, по чужому опыту это как правило компиляции (см книги Столярова), которые тоже могут быть полезны для обучения студентов, но не более
victor_1212
01.10.2022 02:56ps
нетрудно привести примеры хороших оригинальных книг по программированию написанных по собственным работам, начиная с kernighan & ritchie, интересно спросить того кто карму минусовал за это сообщение -
Вы можете привести контрпример, типа хорошей книги по программированию написанной по чужим работам (компиляции) кроме учебников конечно?
0xd34df00d
01.10.2022 04:20+4Вы можете привести контрпример, типа хорошей книги по программированию написанной по чужим работам (компиляции) кроме учебников конечно?
TAPL, например.
victor_1212
01.10.2022 17:41согласен, хорошо написано, и не компиляция конечно, Pierce phd делал в Carnegie Mellon, одна из лучших школ по CS, supercomputing center тоже там, кстати там тоже Gordon Bell работал (digital vp r&d, т.е. прямое отношение к созданию VAX), это к тому что в случае TAPL человек прямо в центре событий находился и сам активно работал, его thesis (Programming with Intersection Typesand Bounded Polymorphism) стоит взглянуть как пример хорошей теоретической работы, в моем понимании TAPL типа осмысление достигнутого в части языков, но в общем Вы правы
ps
пожалуй первоначальный тезис можно переформулировать так - хорошие книги пишутся там, где ведется серьезная работа
F0iL
30.09.2022 23:56+7и есть ли координация с Гугол ринувшимся делать Carbon чисто случайно как только Rust начал набирать популярность?
Гугл говорит, что у Rust и Carbon какбэ разные цели и они не конкуренты:
Existing modern languages already provide an excellent developer experience: Go, Swift, Kotlin, Rust, and many more. Developers that can use one of these existing languages should. Unfortunately, the designs of these languages present significant barriers to adoption and migration from C++. These barriers range from changes in the idiomatic design of software to performance overhead.
Более того, Google один из основных спонсоров инициативы по поддержке разработки на Rust в ядре Linux.
Pastoral
01.10.2022 09:49-7Интереснейшая цитата возбудила моего внутреннего конспиролога того больше.
Как Dart попал в and many more, это же предел мечтаний и свет (Истины) в окошке?
Разве changes in the idiomatic design не должны попадать в категорию goals вместо barriers?
Насколько я понимаю, получив отказ на просьбы пустить проприетарщину в ядро, Гугол теперь строит интерфейсы между собой и ядром Линукс, что должно дать Андроиду ядро посвежее и обновлений поболе и подешевле. Если бы Rust этому не способствовал, Гугол его в ядро не продвигал бы.
«Наверно Rust есть хорошо и Гугол хочет его для себя» - подумал внутренний конспиролог.
AnthonyMikh
01.10.2022 23:37+6есть ли координация с Гугол ринувшимся делать Carbon чисто случайно как только Rust начал набирать популярность?
В FAQ Carbon прямым текстом написано "If you can use Rust, ignore Carbon".
WASD1
30.09.2022 21:26+15Сложность разбора подобных программ —
из которых приведённая одна из самых простых! —
оставлю без комментариев, замечу только, что знакомая программистам на Си тернарная операция тут мимикрирует под обычный условный оператор, чем провоцирует ещё более частое её использование.А статью точно студент писал?
Начал читать и в какой-то момент прям пахнуло нафталином из плохой методички по 30 лет никому не нужному предмету (назидательный тон и оторванные от реальности лет на 20 выкладки):
То есть я понимаю, когда какой-нибудь дедушка в таком тоне пишет - он уже достиг того возраста, когда люди думают, что заню всё. Но блин студент... ну ему бы (чисто по причине опыта в программировании) ещё учиться и учиться, а он назидательную статью в журнал "вестник передовых работников кибернетики" строчит.WraithOW
01.10.2022 00:26+2Вы как будто в универе не учились. Сказано - пояснительная записка на 15 страниц, а у тебя только 8. Что делать? Обмазывать канцеляритом в 6 слоев, разумеется.
Cheater
30.09.2022 22:55+38О, студент-автор статьи прикольно врёт:
let xs = vec!["lorem", "ipsum", "dolor"];
xs.iter().filter(|item| item.ends_with('m')).nth(0)
Да, исходный код стал значительно меньше, вот только получаемый машинный код на порядки вырос, да ещё и стал существенно медленней. Предлагаемая тут сущность, передаваемая в filter, и вовсе является замыканием из мира функционального программирования и представляется в машине весьма нетривиально; желающим предлагается посмотреть получаемый машинный код.
Одна маленькая проблема, он забыл включить флаг оптимизации. Вот дизассемблированный код c "-C opt-level=2". "Желающим предлагается посмотреть" на 2 с половиной ассемблерные инструкции, в которые превратился вызов filter, а также почитать что такое zero cost abstractions в Rust...
V1tol
01.10.2022 00:26+22Самый залайканный первый комментарий на реддите раста к постам типа "код на расте медленнее, чем код на языке X" - "а вы в релиз режиме код собирали?"
vkni
01.10.2022 22:14Это синтетика, всё-таки — там всего три элемента, с которыми llvm делает истинные чудеса. Я даже не сильно удивлюсь, если в очередной редакции он подставит "lorem" вместо этого кода, сделав полное вычисление.
Но, конечно, странно писать фильтр и брать первый элемент в энергичном языке. То есть, кодhead $ filter ( ((==) 'm') . last ) ["lorem", "ipsum", "dolor"]
выглядит разумно. Но уже в Ocaml я бы использовал List.find:
List.find (String.ends_with suffix:"m") ["lorem"; "ipsum"; "dolor"]
Иначе для длинных списков работа далека от оптимальной.
ZyXI
02.10.2022 00:13+1Меня удивило, что при включении оптимизаций он не сократил весь
main
до одногоret
— тут ни о каком lorem речи не идёт,main
же ничего не возвращает, и не имеет никаких побочных эффектов, кроме выделения памяти. Именно так компилятор делает, если заменитьvec!
на&
. Но почему‐то не если убратьvec!
полностью (т.е. заменить тип xs сVec<&'static str>
на[&'static str; 3]
, или на&[&'static str; 3]
в первом случае).AnthonyMikh
02.10.2022 00:33Ну, чисто технически это было бы заменой наблюдаемого поведения, поскольку в ограниченном по памяти окружении выделение вектора на три элемента может завершиться ошибкой из-за исчерпания памяти, а в "оптимизированном" коде такого пути исполнения нету.
Cheater
02.10.2022 02:36+2Думаю дело в том, что когда создаётся локальная переменная типа [] или Vec, а не &[], компилятор должен создать и инициализировать её на стеке (в куче, для Vec), и догадаться что создавать её не надо он может только если к ней применяются константные методы (коим ends_with не является).
Я заменил метод на константный и у меня вариант с Vec скомпилировался в один ret:
(Дисклеймер: Максимально уродский хак, требующий unsafe nightly rust)
#![feature(const_slice_index)] pub const fn ends_with_m(s: &str) -> bool { unsafe { return *s.as_bytes().get_unchecked(s.len() - 1) == 'm' as u8; } } pub fn main() { let xs = vec!["lorem", "ipsum", "dolor"]; xs.iter().filter(|s| ends_with_m(s)).nth(0); }
0xd34df00d
02.10.2022 03:02Не, ну почему, даже в энергичном, но чистом языке разумно ожидать какой-нибудь фьюжн. Энергичность же не означает отсутствие оптимизаций.
vkni
02.10.2022 07:09+1"Оптимизации хрупки, мой друг!"
Реально, я видел подобное же на C++ с векторами и самописным zip'ом с лямбдами. Код совершенно неидеоматический, но компилятор "урезал осетра" до неотличимости от идеоматического кода. Но вот шаг влево, шаг вправо привёл к тому, что компилятор оптимизировать не смог, и получились несколько страниц ассемблера.
Cheater
02.10.2022 03:35+3Но, конечно, странно писать фильтр и брать первый элемент в энергичном языке.
Просто кривой код. То, что нужно автору, называется find:
let xs = vec!["lorem", "ipsum", "dolor"]; xs.iter().find(|item| item.ends_with('m'));
morijndael
02.10.2022 00:52Емнип, это принципиальная позиция у них там, не врубать оптимизации, <sarcasm>ведь настоящий программист и без них может написать все идеально оптимально, а сложный компилятор такому программисту только мешает</sarcasm>
rtemchenko
30.09.2022 23:23+6Падажжите. Где опровержение изначальной цитаты в статье?
Я мимокрокодил. Но вот это "как заставить тупой компайлер сделать то, чего я хочу" сплошь и рядом при работе с "продвинутыми" компиляторами.
Стильное-модное-молодежное разбивается о реалии коммерческой разработки и зачастую годится только для прототипов.
Это я конечно утрирую. Но я был бы осторожен с теми, кто говорит "Долой С, перепишем все на Раст и заживем". Впрочем тех кто хочет все переписать на С я бы тоже обходил стороной. :)
mayorovp
30.09.2022 23:46+6Её и опровергать нечего, это же очевидная неправда.
Дело скорее в том, что компилятор раста сам решает, когда владение "должно" (с его, компилятора, точки зрения) перейти от одного игрока к другому. А решать это вообще-то должен программист, а не компилятор.
При передаче по значению происходит передача владения, если передать ссылку или клонировать объект — передачи владения не происходит. Писать или не писать
&
там,.clone()
или вовсе поставить трейтCopy
решает не компилятор, а программист. Всё.nuclight
01.10.2022 02:22И эти вещи сто лет как называются "make compiler happy" в комментариях в коде, даже на Си. То есть если посмотреть с более высокого уровня, цитата таки права — программист вынужден подстраиваться под компилятор.
lorc
01.10.2022 08:04+9И эти вещи сто лет как называются "make compiler happy" в комментариях в коде, даже на Си.
В 99% таких случаев компилятор всячески пытался предостеречь программиста от ошибки. Просто программист этого не понимал.
Halt
01.10.2022 08:13+17Нет, вы не поняли. Копирование или передача по значению это не прихоть компилятора, а часть семантики языка. Указание ссылки или клонирование это не ублажение компилятора а опять же, необходимые действия для соблюдения контракта вызова. Независимо от компилятора, независимо от оптимизаций и погоды на марсе контракт остается тем же. Компилятор может вырезать операции позже, например при инлайнинге, но это не поменяет контракт.
Эти вещи определены однозначно, как раз таки в отличие от компилятора C++, который, например, может делать copy elision или «продление времени жизни», а может и нет, по желанию левой пятки.
Вот как раз в C++, продление времени жизни является адским хаком, не вытекающим напрямую из семантики, про который, по видимости говорят Столяров и К°, транзитивно перенося его на Раст.
thevlad
30.09.2022 23:31+27Уровень исходной(rustmustdie) статьи пробивает пол, хотя я и не фанат раста. Обычно то, что обозначается в статье, как zero runtime, называется bare metal, потому что именно эмбедерам оно и нужно. И ищется как bare metal rust.
PS: о кто-то обиделся и решил в карму плюнуть ) возможно было недопонимание, поправил, речь шла не о статье на хабре
Deosis
03.10.2022 09:29+4Я бы даже сказал, что rustmustdie - это очень жирный наброс.
К примеру сборщик мусора std::rc::Rc;
Тогда в C++ есть как минимум 3 сборщика мусора (unique_ptr, shared_ptr, auto_ptr)
codecity
30.09.2022 23:33А кто смотрел тонкости лицензирования Rust этого? Не может с ним произойти то же что с Java?
DirectoriX
01.10.2022 05:13+3Компилятор и стандартная библиотека очень даже свободные:
Rust is primarily distributed under the terms of both the MIT license and the Apache License (Version 2.0), with portions covered by various BSD-like licenses.
mrsantak
01.10.2022 09:40А что такого произошло с Java?
vassabi
01.10.2022 13:11у него были Sun и Oracle
mrsantak
01.10.2022 17:45+1Были, а произошло-то что?
vassabi
01.10.2022 22:20было много разного в его истории, чего хотелось бы избежать :)
(ну, если вы не корпорация с толстым кошельком)
siziyman
02.10.2022 17:38+2Это всё, конечно, не самые приятные вещи, которые вообще никак не ограничивают 99,99999% разработчиков на Java. То есть проблемы с прагматической точки зрения пользователя языка не существует, проблема существует разве что этическая.
vassabi
03.10.2022 00:02-2если вы школьник и пишете лабораторные работы - то без проблем. Если вы неуловимый джо или пишете бесплатный опенсурс - то тоже без проблем.
siziyman
03.10.2022 01:27+2Пойду расскажу, ээээ, паре десятков известных мне крупных банков и ещё наверное полусотне (эт так, навскидку, вообще не думая) энтерпрайзов международного масштаба, которые пишут на джаве и _не имеют и никогда не имели_ проблем с лицензированием (потому что невозможно иметь проблемы с тем, чего нет, если вы не претендуете конкретно на платную поддержку Оракла - и хинт - большинство даже в многомиллиардных масштабах на неё не претендуют), IP rights и прочим, и не платили ни копейки, что у них есть проблемы с джавой, а они-то и не в курсе.
Janycz
01.10.2022 00:23-1После прочтения возник такой вопрос: можно ли на Rust писать как на C? Т. е. используя по максимому стандартную библиотеку языка С, без напряга с borrow checker, не парясь над концепциями владения, оборачивая почти все в unsafe и т. д. и т. п.
thevlad
01.10.2022 00:43+7А какой смысл тогда писать на расте, если все его преимущества с ходу отправляются в ведро? А так конечно можно тролейбус_из_буханки.джепег
0xd34df00d
01.10.2022 00:45+7Можно, но зачем?
Janycz
01.10.2022 00:55-6Могут быть разные причины, начиная от того, что просто нравиться, заканчивая тем что надо реализовать некоторую функцию на Rust (и желательно побыстрее, еще вчера), а стандартную библиотеку или плохо знаю, или не знаю.
P. S. На C# я когда-то занимался программированием как на С, просто нравилось так писать.
mayorovp
01.10.2022 01:17+9надо реализовать некоторую функцию на Rust (и желательно побыстрее, еще вчера), а стандартную библиотеку или плохо знаю, или не знаю
Звучит как то, чего нельзя делать ни в коем случае.
А так — сырые указатели есть, что ещё надо сишнику? :-)
Janycz
01.10.2022 01:25Не, ну то, что сырые указатели есть, это уже хорошо. А как в Rust со всякими кастами? Как borrow checker на указателях работает?
snuk182
01.10.2022 07:12+2Работает, как и с любыми другими типами, с поправкой на статический / динамический полиморфизи (типажи в помощь). Касты есть, но они для нубов / отчаянных / бетмена.
kmeaw
01.10.2022 16:28Например для того, чтобы оттранслировать существующий C-проект, а потом, постепенно, переписывать его части, используя возможности Rust. Как режим BetterC в D.
crackidocky
01.10.2022 00:29+10Студент в упомянутой статье еще вроде бы взъелся на синтаксис в Rust. До меня немного не доходит почему. Я в разработке сравнительно немного (3 года) и системным программированием не занимался, но даже для того кто писал три года на TypeScript - синтаксис в Rust достаточно интуитивный.
Более того, чем больше пишешь на Rust, тем больше понимаешь, что синтаксис очень похож на TypeScript.Структуры - интерфейсы с конструктором
Имплементации - все те же интерфейсы
Трейты - абстрактные интерфейсы
В синтаксисе все достаточно легко, даже те же стрелочные функции сделаны достаточно просто
fn main() { let n = 2; let x = 3; let string_concat = |x: i32, n: i32| -> String { x.to_string() + &n.to_string() }; println!("{}", string_concat(x, n)); }
Да и сам студент почему-то вечно говорит про синхронный код, кой его в обычных задачах иногда бывает меньше, нежели асинхронного (HTTP-запросы, сокеты, файлы и так далее)
Также сборщик мусора о котором говорит учащийся - далеко не тот сборщик мусора, что например в JS или Python.
Автору респект за тщательный разбор материала
Из прочего:
Идея того, что в Rust отказались от
null
- просто превосходная, без него намного легче.switch
(тут онmatch
) наконец-то стал выглядеть красиво, а не как огромное количествоcase
Идея с заимствованием понятна с самого начала, сделано для того чтобы никто не изменил переменную без нашего ведома
Там еще учащийся упоминал о том, что непонятно зачем теперь уровни вложенности, если все переменные будут все равно дропаться в конце. Они нужны чтобы не было вот такого:
fn main() { { let x = 32; } println!("{}", x); // Не скомпилируется }
alexxisr
01.10.2022 18:31+6фигасе интуитивный. вот мне как ни разу не писавшему на расте не понятно - зачем println! восклицательный знак? ну предположим, что просто так функцию назвали. Но почему x.to_string() без & а &n.to_string() с ним? и зачем в println! первый аргкмент "{}"? это же явно не формат как в С-ной printf
mayorovp
01.10.2022 18:38+1Восклицательный знак потому что это макрос, а не функция. В первом приближении можно считать что и правда просто функцию так назвали. С аргументом тут тоже просто — это всего-то форматная строка, да не как в printf — но в printf самые плохие форматные строки, не удивительно что другие языки как могут придумывают своё.
А вот с амперсандом и правда хрень какая-то вышла. Нужен он потому что в языке нет оператора сложения двух владеющих памятью строк, есть лишь сложение строки и ссылки на другую строку, соответственно амперсанд тут без всякой магии превращает строку в ссылку. Но почему нельзя было сделать перегрузку для двух строк — для меня тоже загадка.
Alex_ME
01.10.2022 22:07> Но почему нельзя было сделать перегрузку для двух строк — для меня тоже загадка
1. Ну так если оператор будет принимать две владеющие строки, то он заберёт владение обоими. И если левый аргумент возращается как результат конкатенации, то правый аргумент был бы забран и дропнут.
2. При сложении строк нем нужен буфер большего размера. И тут либо выделить новый, либо расширить буфер одной из них (т.е. мутировать одну из них). И текущая семантика это показывает.mayorovp
01.10.2022 22:23Ну так если оператор будет принимать две владеющие строки, то он заберёт владение обоими. И если левый аргумент возращается как результат конкатенации, то правый аргумент был бы забран и дропнут.
А что в этом страшного?
При сложении строк нем нужен буфер большего размера. И тут либо выделить новый, либо расширить буфер одной из них (т.е. мутировать одну из них). И текущая семантика это показывает.
Ну так и предлагаемая семантика это тоже позволяет.
vassabi
01.10.2022 23:55в Rust же можно расширить существующий оператор - написать сложение с двумя владеющими строками?
почему бы не попробовать написать, и потом им попользоваться ?
mayorovp
02.10.2022 00:18Можно, но только в том же крейте в котором определены сами строки (т.е. в стандартной библиотеке).
Т.е. если бы в стандартной библиотеке появился вот такой код:
impl Add<String> for String { type Output = String; fn add(self, rhs: String) -> Self::Output { self + &rhs } }
то и существующий код бы не пострадал, и новый бы чуть менее удивлял.
withkittens
03.10.2022 14:35и новый бы чуть менее удивлял.
Вас бы действительно не удивляло, если бы оператор сложения дропал
s2
? :)fn main() { let s1 = "Hello ".to_string(); let s2 = "world!".to_string(); let s3 = s1 + s2; println!("{}", s2); // oops: error[E0382]: borrow of moved value: `s2` }
mayorovp
03.10.2022 14:47Но ведь он прямо сейчас "дропает" s1. Что, s2 чем-то принципиально отличается?
И нет, это не удивляет если хоть немного привыкнуть к языку. Переменная перемещаемого типа перемещается каждый раз когда используется без амперсанда, сложение тут не исключение.
withkittens
03.10.2022 15:26Конечно. Буфер s1 по возможности будет переиспользован (если там хватает места для s2). Для этого нам нужно отдать владение s1 сложению, взамен мы возможно избегаем лишнего выделения памяти. s2 нужна только для чтения.
И нет, это не удивляет если хоть немного привыкнуть к языку. Переменная перемещаемого типа перемещается каждый раз когда используется без амперсанда, сложение тут не исключение.
Синтаксически всё было бы верно, да. Идея в том, что если я вам даю дискету, чтобы вы с неё скопировали данные, я не хочу, чтобы вы её у меня забирали себе.
AnthonyMikh
01.10.2022 23:55+1Но почему нельзя было сделать перегрузку для двух строк — для меня тоже загадка.
Потому что
str
является примитивным типом и потому методы на нём, включая реализацию трейта core::ops::Add, могут быть только в крейтеcore
. Результатом подобного сложения должна быть выделяющая в куче памятьString
, определение которойпринадлежит крейту alloc
, разумеется, зависящему отcore
. Написать реализацию такого сложения строк вcore
нельзя, потому что в этом случаеcore
зависела бы отalloc
, а циклические зависимости между крейтами недопустимы. Написать же реализацию сложения строк вalloc
нельзя, потому чтоstr
— примитивный тип.В любом случае, в примере выше проще было бы вместо сложения написать
format!("{x}{n}")
. И, кстати, к вопросу о том, почему это макрос, а не функция: с функцией вы не смогли бы интерполировать переменные.
Mingun
01.10.2022 18:58-1Вот вообще странные претензии.
зачем println! восклицательный знак?
А почему в питоне через
%
от форматной строки нужно аргументы перечислять?и зачем в println! первый аргкмент "{}"? это же явно не формат как в С-ной printf
А почему он должен им быть?
mrsantak
01.10.2022 21:52А почему он должен им быть?
А зачем для вывода одной строки вообще какое-то форматирование? Почему нельзя просто передать
string_concat(x, n)
единственным аргументом вprintln!
? Я хз как оно в расте, но в java подобное было бы индусским кода уровняif(value) return true; else return false;
mayorovp
01.10.2022 22:06+1По той же причине почему нельзя передать единственную строку в printf...
Кстати, вот вариант без форматирования:
stdout().write_all(string_concat(n, x).as_bytes()).unwrap();
Проще не получилось...
KivApple
01.10.2022 04:24+1Скажу неожиданный минус Раста - отсутствие нормальной поддержки сишных строк. Подавляющее большинство нативных библиотек имеет Си совместимый интерфейс. А в расте для стандартных CStr/CString даже трейт Display не реализован. И как литерал их из коробки записать нельзя. Предлагается конвертировать строки туда сюда на каждый чих. Что мешало добавить в стандартную библиотеку несколько функций?
snuk182
01.10.2022 07:20+16Ирония в том, что си-строки в Rust поддерживаются лучше, чем в самом си - есть хотя бы собственный тип с базовыми методами, а не просто глупый массив символов. В принципе, и без этих методов и типа жилось бы не сильно хуже, учитывая количество возможных кодировок строк, из-за которых все равно нужно спускаться на уровень глупого массива байт (да, это ответ на вопрос, почему нет Display и прочего визуала).
edo1h
01.10.2022 06:51Намного важнее сам принцип: язык Си позволяет полностью отказаться от возможностей стандартной библиотеки
может быть и позволяет, только пример совершенно не соответствует заявленному «работающая (вызывающая сисколы) программа на си без байта внешних зависимостей».
netch80
01.10.2022 11:06-9> Но это бы лишний раз показало, что преподаватель, который влил эту чушь в голову студентам МГУ, не просто находится не на своем месте, он вообще ничего не смыслит в системном программировании.
Когда оценивают подобное утверждение, надо понимать минимум две вещи:
1. Чему равно «все» = «универсальное множество» в рассмотрении. Потому что всегда есть, например, где-нибудь какой-то наспех сляпанный одним студентом язык, который даёт 1/4 того, что есть в C89, при этом точно так же не требует стандартной библиотеки, но достаточен для написания драйверов. (Или, возьмём пример ещё грубее, язык, использованный марсианами. Мы про них ничего пока не знаем.) Естественно, он не рассматривается, и «все» будет включать «все достаточно известные, чтобы попасть в рассмотрение», и это не будет являться ошибкой автора.
2. Когда именно было написано цитируемое утверждение. Книги Столярова впервые вышли задолго до появления Rust. Такие источники всегда немного отстают от реальности, и всё переписать невозможно.
В сумме, позиция автора статьи показывает или его некомпетентность в вопросе анализа источников — что недопустимо для обладателя высшего естественнонаучного образования, или же существенную личную неприязнь к конкретному преподавателю. Оба фактора не позволяют положительно оценить ни его, ни статью. Надеюсь, что автор осознает и исправится.Quark-Fusion
01.10.2022 17:19-12. образование отстаёт от реальности — поправил
если хотите актуальных знаний не надо туда вообще лезть
я вот может не знаю, образование учит анализировать источники?
netch80
01.10.2022 11:11И ещё:
> Настало время реализовать вывод «Hello, world!» в стандартный поток вывода! \<Не забыть изменить на менее глупую фразу перед публикацией>.
1. Лучше использовать более привычные пометки типа XXX, TODO… — по ним легко искать.
2. А что глупого-то в «Hello world»?
Уж простите, что не про Rust.Browning
01.10.2022 13:23+1Полагаю, это отсылочка к "убрать перед публикацией" из квеста, о котором недавно рассказывали на Хабре: https://habr.com/post/689116/
inklesspen
01.10.2022 11:27rustmustdie до сих пор кажется скорее шуточной статьей, нежели чем-то серьезным. В другой статье, cmustdie, где Алексей является соавтором, приводятся более адекватные (на мой взгляд) аргументы.
Очень надеюсь, что через некоторое время автор скажет что-то на подобии "Расходимся, это была шутка".
Apoheliy
01.10.2022 13:12Прочитал. Оценил важность делать syscall "без нифига" (это действительно нужно? Для C? И для условного JavaScript-а тоже (как он только ещё живёт-то без этого)?).
Далее ! САРКАЗМ !:
Впечатление как об каком-то обществе защиты жуков-выберите-любой:
А-а-а, общество куропаток назвали нашего жука хуже бабочек-однолеток! Как они посмели!
Однако наш жук лучше: посмотрите, какие у него шейные мышцы!
Конечно же, в интернете кто-то неправ. Но почему все наезжают на нашего жука! Вот прям все-все-все!
Это сильно напоминает стенания: про наших жуков забы-ы-ы-ы-ли!!! Как вы могли-и-и???
конец сарказма.
По мне, у каждого есть любимый язык программирования. Однако устраивать из этого соревнования с набрасыванием на вентилятор - это, по-моему, уже холивар.
Возможно, будущие статьи писать без противопоставления с другими языками. В стиле:
Посмотрите, какие прикольные штуки умеет Rust? Он может работать и без стандартной либы!
vassabi
01.10.2022 13:15+1Я бы хотел, чтобы в МГУ (самом МГУ!) ученые и студенты были открыты к познанию. Ведь в этом и есть суть университетов, нет? Слишком многого хочу?..
хмм .. а представьте что завтра к вам университет придет и скажет "мы хотим, чтобы вы открыли нам глаза и пришли к нам на кафедру. Будете у нас вести курс, вместо вот этого косного и закрытого к познанию нехорошего человека" - они многого хотят или норм ? :)
sshikov
01.10.2022 16:05+5Вопрос скорее в том, деньги где у них на такие хотелки? Обычно этим все и заканчивается.
kuraga333
01.10.2022 14:31А я вот не очень понял, почему rustc_codegen_gcc генерирует нечто другое, чем до его использования? В чём именно различия будут? Спасибо.
steanlab
01.10.2022 15:16+3Автору заслуженный плюс. И за статью, и за мотивацию к ее написанию
p.s. сам ни разу не кодер, ни разу не знаком с rust, но прочел на одном дыхании вместе со всеми спойлерами :)
jok40
01.10.2022 18:02+8Считается, что 93.9% программистам именно такое поведение (автоматическое включение std и прелюдии) и требуется.
Тем не менее, для остальных 19% программистов предусмотрен режим отключения стандартной библиотеки с помощью атрибута #![no_std]
Вы же не думаете, что я тут беру статистику с потолка?
Эээ… Нет, конечно, не думаем ни в коем случае! :)
valeriyk
01.10.2022 20:36+2Кстати, раз уж прописали panic = "abort", то
extern "C" fn eh_personality() {}
не нужна.
Cykooz
01.10.2022 22:26Finished dev [unoptimized + debuginfo] target
Я конечно понимаю, что вероятно в приведённом коде практически нечего оптимизировать, но можно же было, хотя бы для порядка, собирать релизную версию бинарного файа, а не отладочную.
gregorybednov
02.10.2022 13:02+4Было занимательно прочитать!
Кстати, если рассматривать не только Rust, то окажется, что на поверку утверждение А.В.Столярова ещё более ограниченное в плане рассмотрения.
Ведь помимо традиционных высокоуровневых языков программирования, "уникального" C и языков ассемблера есть ещё Forth, чей "рантайм" для любой конкретной архитектуры железа программируется в том числе на голое железо даже на языках ассемблера без особых проблем с нуля за несколько дней работы (уточнение: при должном знании целевой архитектуры), после чего можно наращивать кодовую базу программируя в сравнительно высокоуровневом ключе. Более интересная эквилибристика - это написать форт-машину под множество архитектур, тут наиболее успешным стал проект CollapseOS. Еще из примечательного, этот язык программирования используется в загрузчике FreeBSD.
Всё это тоже выбивается из парадигмы, озвученной в начале статьи, хотя и не относится к языку Rust
WASD1
02.10.2022 13:37+4Коллеги кто-нибудь может мне объяснить "за феномен Forth".
Часто вижу вот такие вот восхваления про форт и блоги отдельных исследователей форсящих его.
Но язык 1971 года не ставший популярным (да ещё и построенный на стековой машине - моде 1970х годов) меня очень смущает.
Кто-то из инженеров может пояснить: есть что-то реально стоящее в языке (ну условно Haskell можно изучить для полезного расширения кругозора, Rust - для полезного изучения кругозора и для программирования) для его изучения или просто у языка есть группа преданных фанатов?gregorybednov
02.10.2022 16:57+3Надеюсь, что Вам ответят, так как ситуация парадоксальная - упрощая, все люди делятся на тех, кто Форт не знает; тех, кто от него фанатеет и наконец тех, кто его ненавидит.
Поскольку я принадлежу ко второй группе, то не претендуя на объективность, постараюсь расписать достоинства и не забыть об основных недостатках.
Это язык программирования, машина исполнения которого понятна. В мире, где для одной из самых популярных компьютерных архитектур даже язык ассемблера в каком-то смысле "компилируемый", а не транслируемый (здравствуй, микрокод x86), где даже виртуальные машины интерпретируемых и JIT-компилируемых языков сложны, по-моему это приятная "отдушина".
На более высоком уровне (как самого языка, так и владения им) Форт предлагает метапрограммирование, которое резко отличается от ООП в стиле C++ (и всех его потомков)
Таким образом, язык похож на Лисп, но с некоторыми фичами (низкоуровневость, миниатюрность и исполнение на голом железе - очень удобно для микроконтроллеров; ещё есть интересные свойства из-за конкатенативности, так по идее можно например упрощать выражения). Также эта простота позволяет использовать его как основу примитивных DSL-процессоров, которые могут, например, обладать повышенной надёжностью (чем проще система, тем надёжнее) и использоваться, скажем, в космосе, или работать в наноботах, и т.п. нетривиальные задачи. Также идейный потомок Форта реализован в контроллере практически любого принтера (PostScript, привет!) и вообще это очень неплохо для встраиваемой техники.
Есть и, казалось бы, очевидные недостатки: чаще всего программы на Форте падают от одной-единственной ошибки, многие программы с нуля пишутся как головоломка. Но тут важно понимать, что это не объективная проблема, а несовместимость с современным ИТ и методологией разработки - но и сейчас бывают и такие программы, в которых не должно быть ошибок, а вместо "программ с нуля" (каждая из которых в 80ые-90ые, как правило, была от одного вендора) иногда правда лучше работать с именованными функциями (словами).
Мне очень понравилось определение Форта как "структурированного ассемблера": языка ассемблера некоторой стековой машины (возможно виртуальной, возможно физической), позволяющего без обыкновенных (для ассемблеров) проблем писать структурированный код.
Но повторю, я скорее из "преданных фанатов" :D , но старался расписать как можно объективнее.
Tell997
02.10.2022 14:01+1Моё уважение автору!) Статья получилось подробной, интересной и захватывающей! На самом деле да -- не стоит следовать советам знаменитых людей, самому не проверив информацию. В современном мире вообще следует подвергать всё сомнению
erlyvideo
02.10.2022 17:55+4Я в этом самом МГУ учился и Столярова видел.
Мне очень интересно, когда и в какой момент ВМК МГУ стал авторитетом в области программирования.
Когда я учился там почти 20 лет назад, я наблюдал давление на тех, кто пытался принести что-то новое. Учитывая, что имена не поменялись, непонятно с чего это давление должно было исчезнуть.
Буду рад увидеть какие-то значимые достижения МГУ (и в частности ВМК) в практической области программирования. Аутсорсное батрачение на западные компании (что преподносилось как очень большое достижение) просьба не показывать, не релевантно.Cerberuser
02.10.2022 20:43+2Не могу не попытаться призвать в тред товарища @3Dvideo- памятуя о прослушанном (к своему стыду, наполовину) курсе его авторства, думаю, их кругу будет что ответить.
JustForFun88
02.10.2022 23:13+4Что мне иногда раздражает, так это то что многие говорят под капотом rust использует много unsafe, и что нельзя превратить
unsafe
вsafe
.Хотя вот пример как из превратить
unsafe
вsafe
и получать PROFIT :-). Привожу адаптированный код хеш таблицы из библиотекиhashbrown
:pub struct RawTable<T, A: Allocator + Clone = Global> { table: RawTableInner<A>, // Tell dropck that we own instances of T. marker: PhantomData<T>, } pub struct RawTableInner<A> { bucket_mask: usize, ctrl: *const u8, growth_left: usize, items: usize, alloc: A, } pub fn get<F>(&self, hash: u64, eq: F1) -> Option<&T> where F: FnMut(&T) -> bool, { match self.find(hash, eq) { // bucket = *mut T Some(bucket) => Some(unsafe { &*bucket }), None => None, } }
Прикол в том что вроде бы как RawTableInner хранит сырой указатель на аллоцированную память и полностью небезопасен. Но далее привязываем данную таблицы к RawTable + PhantomData что говорит компилятору что у нас владеющая структура. Потом определяем метод get, который внутри использует метод find. Find возвращает сырой указатель. Но!!! Мы ведь определили метод get у &RawTable, а также привязали выходную ссылку на значение к времени жизни самой RawTable, то есть выходная ссылка будет всегда валидна, так как нельзя вернуть ссылку на удаленную таблицу.
То есть, метод find который возвращает полностью небезопасный "сырой указатель" который может существовать даже после удаления RawTable, и по определению unsafe, превратился полностью в safe. Так как с помощью метода get мы явно привязали жизнь сырого указателя к жизни RawTable. Кроме того мы привязали его не просто к таблице. Мы еще и заблокировали изменения самой RawTable, пока существует на него ссылка. И все это в 10 строк кода (функции get).
Вот так мы и переходим из
unsafe
в полеsafe
. Далее уже компилятор проверяет, что ссылка которую мы вернули живет не дальше чем сама структура хеш таблицы, не даст изменить хеш таблицу при наличии действующей ссылки (например вставить новый элемент, что потенциально может привести к аллоцированию нового блока памяти и перевода элементов соответственно в другую область памяти, что автоматический делает висящими все существующие ссылки).
mikhanoid
02.10.2022 23:19+2Именно это свойство — zero runtime — делает Си единственным и безальтернативным кандидатом на роль языка для реализации ядер операционных систем и прошивок для микроконтроллеров. Тем удивительнее, насколько мало людей в мире этот момент осознают; и стократ удивительнее то, что людей, понимающих это, судя по всему, вообще нет среди членов комитетов по стандартизации (языка Си)…
— А. В. Столяров
Нет взаимосвязи между наличием у языка runtime и возможностью реализации на этом языке ядер операционных систем и прошивок для микроконтроллеров. Я знаю о проектах на диалектах Lisp, но, наверняка, есть проекты и на других языках. На Lisp написаны: Mezzano, Loko Scheme, Maker Lisp, LisPi, PilOS, PICOBIT и т.д.
Хотелось бы выразить робкую надежду на то, что пока сообщество Rust самозабвенно борется против C за право занять место царя горы традиционного софта на основании малозначимых свойств языка, где-нибудь в стороне от этой эпической схватки вырастут более изящные системы с более простыми, эффективными, легковесными и разумными API и IPC...
AnthonyMikh
03.10.2022 16:19на основании малозначимых свойств языка
Возможность просто писать программы так, чтобы они не разваливались от малейшего дуновения — это малозначимое свойство?
eoanermine
Писать огромное количество кода только ради того, чтобы смочь сложить два и два без лишнего рантайма — это, конечно, прикольно.
NickSin
Ну с другой сторон это красивое оспаривание тезисов из исследуемой статьи. Вообще, если упороться, можно все в двочиных кодах писать /s
Автору респект за "вызов" было интересно читать.
Didimus
В двоичных кодах два на два умножить не так уж много текста надо
sim31r
Операционная система не пустит к процессору с минимальным текстом. На микроконтроллере да, 2-3 байта и весь код.
WASD1
Не понял почему человека замунусовали?
Он немного кричаще выразился - но суть-то что соблюсти OS API по вызову нового процесса (формат elf или exe или чего ещё) и это не 4 байта в итоге - верна.