1,052 показания
1,052 показания

Трябва ли да научите Rust и Zig? Да, да, трябва

от Ace — The JS Hater9m2025/04/02
Read on Terminal Reader

Твърде дълго; Чета

Стойността на езиците за програмиране, които не крият детайлите.
featured image - Трябва ли да научите Rust и Zig? Да, да, трябва
Ace — The JS Hater HackerNoon profile picture

Езици, които разкриват детайлите

Когато повечето хора започнат да програмират, те са привлечени от езици, които правят нещата лесни. Python, JavaScript и други езици на високо ниво абстрахират бъркотията от детайлите на управлението на паметта, системните повиквания и хардуерното взаимодействие. Тази абстракция е мощна - позволява на начинаещите да създават полезни програми бързо, без да се затъват в подробности за изпълнението.


Но има значителна стойност в езиците, които ви принуждават да се изправите срещу тези подробности. Езици като Rust, C и Zig не само ви правят по-добър програмист на тези специфични езици – те задълбочават разбирането ви за това как всъщност работят компютрите. Това разбиране ви прави по-ефективни във всеки език, който използвате, дори и тези на високо ниво.


За да демонстрираме, нека вземем „проста“ концепция като четене на въведени данни от потребителя и съхраняването им в променлива, след което да демонстрираме как ще бъде направено от езици от по-високо към по-ниско ниво. Ще започнем с най-високото от всички:

Python

 name = input("What is your name?\n") print(name) #Ah, the classic I/O example


За един обучаем, какви могат да бъдат въпросите и обучението тук? Не забравяйте, че ние не просто се опитваме да изработим код, но всъщност да имаме представа какво се случва:


  • Променливи и памет: Имаме „променливи“, които съхраняват данни.

  • Типове данни и памет: Имаме типове данни, а низовете са просто нормален текст. Един много любопитен обучаем може дори да научи за другите типове данни от тази следа.

  • Извиквания на функции: Можем да извикваме функции с аргументи и да съхраняваме резултатите от тези функции в променлива.

  • Среда за изпълнение Програмите на Python могат да се изпълняват чрез извикване на интерпретатора и с програмата (ако приемем, че имаме инсталиран Python; няма да бъркам в мечката, която е версията, зависимостите и инсталацията на Python). Това може да доведе до откритие за интерпретирани срещу компилирани езици.


Тези не са лоши; Мисля, че най-голямото знание за компютрите ще дойде от това малко '\n' в низа. Проучването малко на това би довело до знания за ASCII, UTF-8 и представянето на текст в компютъра като байтове. Вероятно би било твърде много за начинаещи, но ще им даде представа как текстът преминава към 0 и 1. Тук също има малък урок за интерпретатори и компилатори, но това ще изисква значително копаене.

Javascript/Typescript (възел)

 import readline from 'readline/promises'; const rl = readline.createInterface({ input: process.stdin, output: process.stdout, }); const name = await rl.question('What is your name?\n'); rl.close(); console.log(name); //Oh, js, so horribly wonderful in your ways


В допълнение към предишните прозрения, нека оценим какво би могъл да види един любопитен учащ, като просто проучи този код:


  • Входно/изходни потоци : Виждаме изрични препратки към stdin и stdout. Едно просто изследване на тях би довело до файловите потоци stdin и stdout в Unix-базирани среди и може би дори до файлови дескриптори и „всичко е файл“ в Linux системи.


  • Процеси: Виждането на обекта Process може да накара любопитен човек да разбере за процесите и да надникне в процеса на изпълнение на съвременните операционни системи. Въпреки че може да не го разберат напълно, те вече имат идея.


  • Asynchronous I/O : await и Promises запознават обучаемия с това как компютрите обработват операции, които не завършват веднага, и може би дори въпрос защо не се изпълнява просто по директен начин (като Python). Те водят към обучение за:


    • Синхронно и асинхронно изпълнение, неблокиращ I/O и може би едновременност

    • Promises, Microtask Queue и Task Queue в Node, управлявано от събития програмиране и неговите предимства.


  • Създаване на интерфейс и управление на ресурсите: Създаването и затварянето на интерфейс води до въпрос и разбиране на управлението на ресурсите, особено за важни ресурси като I/O потоци.


  • Ключови думи за деклариране ( let , const ): Те не се отнасят изрично към по-дълбоки концепции, но учат на добри практики за контролиране на променливостта.


  • Среда за изпълнение: JS програмите се изпълняват чрез среда за изпълнение, Node, Bun, Deno и т.н. Задачата на средата за изпълнение е да предостави на V8 (JS двигателя) допълнителни функции, за да го превърне в завършен език. Човек може би може да постави под въпрос какво точно предоставят тези времена на изпълнение на двигателя V8 и това би довело до внедряването на асинхронен I/O.


Някои от тях, като Promises и Queues, на пръв поглед са абстракции, свързани с JS, но ако човек в крайна сметка намери пътя си до Libuv – C библиотеката, която обработва асинхронен I/O за Node.js – той ще научи малко за I/O в операционните системи.

C-Sharp

 Console.WriteLine("What is your name?"); string? name = Console.ReadLine(); Console.WriteLine(name); //Surprise!! No public static void Main(string[] args)


Обичайните заподозрени в кодирането на знаци са тук, макар и затъмнени от ReadLine и WriteLine , освен тях се появяват две важни неща:


  • Статично въвеждане и изрични типове : Въпреки че извеждането на типове е прекрасна функция за продуктивност, поддържам мнението, че изричното писане на типове подобрява процеса на учене, особено за начинаещи. Тук обучаемият може да придобие първата си истинска представа за оформлението на паметта, особено когато изследва причините за изричното специфициране на типовете променливи. Те включват резервирането на специфичен брой байтове за определени променливи и грешките, които възникват, когато се опитате да поберете 64 байта в 32 байта памет.


  • Нулеви типове: Те научават, че е възможно място в паметта да има липса на валидна стойност, което допълнително подобрява изгледа на паметта.


  • Един наистина любопитен човек би започнал да пита - Защо трябва изрично да посочваме nullable типове? Има ли някакви конкретни проблеми, които произтичат от третирането на нулеви стойности като не-нулеви стойности в програмите? Това води до научаване на правилата за защита на паметта.


  • Common Language Runtime (CLR), Intermediate Language (IL) и JIT: .NET runtime прави процеса на компилиране по-очевиден, като принуждава обучаемия изрично да изгради и след това да изпълни кода.


Принуждаването на потребителя да компилира своя код му позволява да види генерирания IL. Това ни позволява първият ни поглед към асемблирането (псевдо-асемблирането, така или иначе), инструкциите и регистрите. Има и потенциал да научите за компилирането точно навреме на CLR, ако обучаемият бръкне малко по-навътре в капака.


Въпреки че тези концепции съществуват в други езици, разликата е, че излагането им на потребителя им позволява незабавно да проникнат по-дълбоко и да придобият представа за това какво наистина се случва при изпълнението на кода.


И накрая, I/O е по-абстрахиран тук, отколкото в JS. Нямаме нищо свързано с потоците и управлението на ресурсите.

Голанг

Съжалявам, Gophers, но не мога да покрия всичко или тази статия ще стане твърде дълга.

Ръжда

 use std::io; fn main() { println!("What is your name?"); let mut name = String::new(); io::stdin() .read_line(&mut name) .expect("Failed to read line"); println!("{}", name); } //Almost a 1:1 from The Book


За един умерено любопитен обучаем, какво може да се разбере за системните концепции:


  • Изрична променливост : ключовата дума mut показва, че променливите са неизменни по подразбиране. Отново контрол върху променливостта на данните за всичките му предимства.


  • Изрична обработка на грешки : .expect() показва, че I/O може да се провали и налага разглеждане на обработката на грешки. Това почти се приема за даденост в по-високите езици и обучаемият може да разбере, че взаимодействието с физическите устройства може да доведе до множество грешки, за които човек може да не мисли, ако не бяха представени. Като пример, опитайте да попитате разработчик на база данни дали дисковете са перфектни.


  • Директен достъп до поток : io::stdin() изрично разкрива взаимодействие с I/O ресурси на ниво OS. Точно както преди, това позволява по-дълбоко навлизане в I/O концепциите в операционната система, като разликата тук е, че нещата са много по-голи, отколкото бяха в JS.


  • Разпределение на паметта : String::new() показва първата ни, макар и псевдо-явна, среща с купчината и стека, две от най-важните концепции в паметта. Въпреки че не е много ясен, той дава достатъчно подсказка, че любопитният обучаем може лесно да започне да изследва паметта и да задава въпроси като - „Защо се нуждаем от различни области на паметта?“ "какво е купчината?" и т.н.


  • Препратки и заемане : &mut name разкрива нашето първо изрично въведение към указателите . Въпреки че всеки език досега е използвал препратки под капака, излагането му отпред и в центъра на програмиста им позволява да започнат да придобиват по-задълбочени идеи за оформлението на паметта. Те научават, че можем да използваме едни и същи данни в множество региони, като просто използваме препратки, заедно с предимствата и опасностите от такъв подход.


  • Компилатори, изпълними файлове и асемблиране: Още веднъж, изричното изискване на стъпка на компилиране кара обучаемия да започне да изследва процеса на компилиране, но този път те имат шанс да видят проучването до момента на асемблиране на инструкциите и малко за процеса на изпълнение на съвременните процесори.


Дори и да се чувствате комфортно с абстракциите на високо ниво, експериментирането с един малък елемент в Rust може да освети цял свят от системно поведение, което остава скрито в други езици. Повечето от тях не са нови, така да се каже, разликата е, че тук те са изложени на програмиста, принуждавайки го да мисли и да научава за тях. Това наистина носи допълнителни разходи и трудности, но това се възнаграждава от по-задълбочено разбиране и следователно власт над ресурсите на системата.

Зиг

 const std = @import("std"); pub fn main() !void { var debugAllocator = std.heap.DebugAllocator(.{}).init; defer std.debug.assert(debugAllocator.deinit() == .ok); const allocator = debugAllocator.allocator(); const stdout = std.io.getStdOut().writer(); const stdin = std.io.getStdIn().reader(); var name = std.ArrayList(u8).init(allocator); defer name.deinit(); try stdout.print("What is your name?\n", .{}); try stdin.streamUntilDelimiter(name.writer(), '\n', null); try stdout.print("{s}\n", .{name.items}); } //lol, the code block doesn't have support for Zig


ЗАБЕЛЕЖКА: Наистина обсъждах дали да включа, или не, разпределени в купчина „растящи“ низове или просто да имам много голям, разпределен в стека „статичен“ низ, но тъй като използвах „развиваеми“ низове за всеки друг пример, ето ни. За да обясня просто, нарастващ низ може да расте с допълнителен вход, докато статичен низ е фиксиран - единственият начин да добавите нови знаци е да създадете нов с новия знак.


О, момче, откъде да започнем? Ако обучаемият погледне този код, вероятно ще се изплаши и ще избяга, но какво може да се научи за системните концепции, като се проучи това:


  • Разпределители и памет: Zig изрично ни казва, че когато трябва да получим памет от купчината, трябва да декларираме намеренията си да го направим; това не е толкова абстрактно за нас, колкото в Rust. Ето, разголено е. Въпреки че това добавя повече първоначални разходи, то подтиква разработчика да започне да изследва стека, купчината, динамичната памет, системните извиквания на ОС и защо дори трябва изрично да заделяме и освобождаваме памет. Това допълнително укрепва разбирането на разработчика за структурата на паметта.


  • Почистване и откриване на течове: defer за почистване на паметта и проверките за течове на памет са добри отправни точки, за да научите от първа ръка проблемите, които възникват от неправилно управлявана памет. На този етап разработчикът е достатъчно оборудван, за да навлезе наистина дълбоко в тази тема.


  • Низ, срезове и препратки: Низовете са просто указатели към масив от u8 стойности. Това премахва последната част от абстракцията между концепцията за „низ“ на високо ниво и идеята за „масив от байтове“ на ниско ниво.


  • Директен достъп до I/O потоци: Отново, като прави тези неща видими, разработчикът разбира какво се случва , когато чете или пише от I/O извън програмата.


  • Входно/изходни грешки и обработка на грешки: Извикванията на входно/изходни устройства — и системни извиквания като цяло — могат да се провалят. Разработчикът трябва да проучи и да е наясно с това.

C и C++

Мисля, че разбирате идеята, която казвам тук; няма нужда да биете мъртъв кон.


И така, ето го. Проста задача на няколко езика. Надявам се, че сте се убедили в моята теза. Преди да продължим обаче, нека изясним няколко неща:

И така, просто напишете, пренапишете го в Rust?

Не, отговорът е не - просто не. Излагам тези аргументи от гледна точка на човек, който иска да научи повече за това как работят компютрите и този, който трябва да изцеди всичко от вашия хардуер (можете също да отидете на асемблиране, като момчетата от FFMPEG за това, ако искате).


Но какво се случва, когато ви е добре да пожертвате част от ефективността в името на скоростта на разработка? Какво ще стане, ако трябва да напишете лек уеб сървър с известна логика за ден или два? Какво се случва, когато сте нов разработчик, който е толкова уплашен от C++, че иска да изостави код?


Има много ситуации, за които нещо като Go, Elixir, Haskell или каквото и да е друго е добре. Моля само след това да отделите малко време и да научите малко за това, което наистина се случва; не е нужно да сте магьосник от ниско ниво, който може да пише asm в съня си, но знанието какво се случва с компютрите ще ви помогне да пишете по-добър, по-производителен код . И ще ви помогне да спрете да виждате компютъра си като черна кутия. Обещавам също, че ще ви хареса.


Говорете с мен в Twitter .

L O A D I N G
. . . comments & more!

About Author

Ace — The JS Hater HackerNoon profile picture
Ace — The JS Hater@ace2489
Software dev | Web3 with Rust/Solidity | Systems dev with Rust/Zig. Reach me at [email protected]

ЗАКАЧВАЙТЕ ЕТИКЕТИ

ТАЗИ СТАТИЯ Е ПРЕДСТАВЕНА В...

Trending Topics

blockchaincryptocurrencyhackernoon-top-storyprogrammingsoftware-developmenttechnologystartuphackernoon-booksBitcoinbooks