چرا زیگ وقتی در حال حاضر C++،D و Rust وجود دارد؟
بدون کنترل پنهان
اگر کد زیگ به نظر نمی رسد که برای فراخوانی یک تابع پرش می کند، پس اینطور نیست. این بدان معناست که می توانید مطمئن باشید که کد زیر فقط foo()
و سپس bar()
را فرا می خواند، و این بدون نیاز به دانستن انواع هر چیزی تضمین می شود:
var a = b + c.d;
foo();
bar();
نمونه هایی از کنترل پنهان:
- D دارای توابع @property است، روش هایی که شما با دسترسی به فیلد تماس می گیرید، بنابراین در مثال بالا، c.d ممکن است یک تابع را فراخوانی کند.
- C++، D و Rust دارای سربارگذاری عملگر هستند، بنابراین عملگر + ممکن است یک تابع را فراخوانی کند.
- C++،D و Go دارای استثناء throw/catch هستند، بنابراین ممکن است foo() یک استثناء ایجاد کند و از فراخوانی bar() جلوگیری کند.
(البته، حتی در Zig
foo()
می تواند منجر به بن بست داده شود و از فراخوانیbar()
جلوگیری کند، این می تواند در همه زبان های Turing-complete اتفاق بیفتد.)
هدف از این تصمیم طراحی بهبود خوانایی است.
بدون تخصیص حافظه پنهان
Zig در مورد تخصیص استک رویکرد عملی دارد. کلمه new
وجود ندارد
یا هر ویژگی از زبان دیگری که از یک تخصیص کننده استک استفاده می کند (به عنوان مثال عملگر اتصال رشته [1]).
کل مفهوم heap توسط کتابخانه و کد برنامه مدیریت می شود، نه توسط زبان.
نمونه هایی از تخصیص حافظه پنهان:
defer
در Go حافظه را به یک تابع محلی اختصاص می دهد. علاوه بر اینکه غیرقابل تصور است در صورت کارکرد این جریان، در داخل یک حلقهdefer
میتواند باعث خرابی حافظه شود- coroutines ++C به منظور فراخوانی coroutine، حافظه استک را اختصاص می دهند.
- در Go، فراخوانی توابع میتواند منجر به اختصاص حافظه heap شود، چون goroutines ها حافظه هایی کوچک را در استک اختصاص میدهند. وقتی استک فراخوانده میشود به اندازه کافی عمیق می شود و تغییر اندازه می دهد.
- API های کتابخانه استاندارد Rust در شرایط خارج از مموری وحشت میکنند، و API هایی که پارامترهای تخصیص دهنده را می پذیرند، قابل تأمل است (اینجا را ببینید rust-lang/rust#29802).
تقریباً همه زبانهای دارای زباله جمع کن دارای تخصیص حافظه پنهانی هستند که از آن زمان پخش شده است زباله جمع کن ها شواهد را پنهان می کنند.
مشکل اصلی تخصیص های پنهان این است که از قابلیت استفاده مجدد یک قطعه کد جلوگیری می کند، و بدون محدودیت تعداد محیط هایی را که کد قرارگرفته را محدود می کند. موارد استفادهای وجود دارد که جریان کنترل و فراخوانی های توابع نباید تاثیری بروی تخصیص حافظه داشته باشند. بنابراین یک زبان برنامه نویسی تنها در صورتی می تواند به این موارد استفاده کند که واقع بینانه باشد. این ضمانت را ارائه دهید.
در Zig،ویژگیهایی در کتابخانه استاندارد وجود دارد که تخصیص دهندگان heap را ارائه می دهد و با آنها کار می کند، اما این ویژگی های کتابخانه استاندارد اختیاری است که در خود زبان ساخته نشده است. اگر هیچ وقت یک تخصیص کننده heap را آماده نکنید، می توانید مطمئن باشید که برنامه شما تخصیص زیادی را انجام نمی دهد.
همه قابلیت های کتابخانه استاندارد برای اختصاص حافظه heap نیاز به پارامتر Allocator
دارند.
به منظور انجام آن این بدان معناست که کتابخانه استاندارد Zig از اهداف مستقل پشتیبانی می کند. برای
مثال std.ArrayList
و std.AutoHashMap
را می توان برای برنامه نویسی bare metal استفاده کرد!
تخصیص دهندگان سفارشی مدیریت حافظه دستی را بسیار راحت می کند. Zig یک Allocator اشکال زدایی دارد که امنیت حافظه را حفظ میکند و از ایجاد اشتباهاتی مثل استفاده پس آزاد کردن و آزاد کردن دوباره جلوگیری میکند. همچنین به صورت خودکار ردیابی نشت حافظه را شناسایی و چاپ می کند. Arena Allocator وجود دارد تا بتوانید همه تخصیص ها را در یک allocator قرار دهید و به جای مدیریت همه، آنها را یکجا آزاد کنید. هر تخصیص به طور مستقل از allocator ها می تواند برای بهبود عملکرد یا از حافظه برای نیازهای هر برنامه خاص استفاده کرد.
[1]: در واقع یک عملگر اتصال رشته (عموماً یک عملگر اتصال آرایه) وجود دارد، اما فقط در زمان کامپایل کار می کند، بنابراین هنوز هیچ تخصیص heap در زمان اجرا را انجام نمی دهد.
پشتیبانی درجه اول از کتابخانه های غیر استاندارد
همانطور که در بالا اشاره شد، Zig دارای یک کتابخانه استاندارد کاملا اختیاری است. فقط رابط کتابخانه استانداردی که استفاده کرده اید با برنامه شما کامپایل میشود. همچنین Zig پشتیبانی برابری با لینک کردن libc یا نکردن آن دارد. Zig با برنامه نویسی high-performance و bare-metal بسیار دوستانه است. این روش برای هر دو جهان بهترین است؛ برای مثال در زیگ، برنامههای WebAssembly میتوانند از آن استفاده کنند. ویژگیهای نرمال کتابخانه استاندارد، هنوز هم به باینری های کوچک منتج میشود. این در مقایسه با سایر زبانهای برنامهنویسی که از کامپایل تا WebAssembly پشتیبانی میکنند.
زبان قابلحمل برای کتابخانهها
یکی از اهداف اصلی برنامهنویسی، استفاده مجدد از کد است. متاسفانه، در عمل، ما بارها و بارها این چرخ را دوباره اختراع میکنیم.
- اگر یک برنامه دارای الزامات real-time باشد، پس هر کتابخانهای که از زباله جمع کن یا هر رفتار غیر قطعی دیگری استفاده کند، به علت وابستگی رد صلاحیت میشود.
- اگر یک زبان آن را بیش از حد آسان میسازد که اشتباهات را نادیده بگیرید، و در نتیجه دقت کنید که یک کتابخانه به درستی کنترل شود و خطاها را ایجاد کند، میتواند اغوا کننده باشد تا کتابخانه را نادیده بگیرد و آن را دوباره اجرا کند، چون میداند که یکی تمام اشتباهات مرتبط را بدرستی انجام دادهاست. زیگ به گونهای طراحی شدهاست که یک برنامهنویس تنها میتواند خطاها را به درستی کنترل کند و بنابراین میتوان مطمئن بود که یک کتابخانه به درستی خطاهای حبابی ایجاد میکند.
- در حال حاضر به لحاظ عملگرایی درست است که C همه کاره ترین و قابل حمل ترین زبان است. هر زبانی که توانایی تعامل با کد C را نداشته باشد، در معرض تاریکی قرار می گیرد. Zig در تلاش است تا با تطابق مستقیم با C ABI برای عملکردهای بیرونی و معرفی ایمنی و طراحی زبان که مانع از اشکالات رایج در برنامه ها می شود، به زبان جدید قابل حمل کتابخانه ها تبدیل شود.
مدیر بسته و سیستم ساخت برای پروژه های موجود
Zig یک زبان برنامه نویسی است، اما همچنین دارای سیستم ساخت و مدیریت بسته است که حتی در زمینه یک پروژه سنتی C/C++ مفید است.
نه تنها می توانید کد Zig را به جای کد C یا C++ بنویسید، بلکه می توانید از Zig به عنوان جایگزینی برای ابزارهای خودکار، cmake، make، scons، ninja و غیره استفاده کنید و علاوه بر این، یک مدیر بسته ( وابستگی های بومی این سیستم ساخت مناسب است حتی اگر کل پایگاه داده پروژه در C یا C++ باشد.
مدیران بسته های سیستم مانند apt-get، pacman، homebrew و دیگران برای تجربه کاربر نهایی مثر هستند، اما می توانند برای نیازهای توسعه دهندگان کافی نباشند. یک مدیر بسته، بسته به زبان می تواند تفاوت بین نداشتن مشارکت کننده و داشتن تعداد زیادی باشد. برای پروژه های منبع باز، مشکل ساختن پروژه به طور کلی یک مانع بزرگ برای مشارکت کنندگان احتمالی است. برای پروژه های C/C++، وابستگی می تواند کشنده باشد، به ویژه در ویندوز، جایی که هیچ مدیر بسته ای وجود ندارد. حتی هنگام ایجاد Zig خود، اکثر مشارکت کنندگان بالقوه با وابستگی به LLVM مشکل دارند. زیگ راهی برای وابستگی مستقیم پروژه ها به کتابخانه های بومی ارائه می دهد - بدون اینکه بسته به مدیر بسته سیستم کاربران، نسخه صحیح آن را در اختیار داشته باشد، و به نحوی که عملاً تضمین می شود پروژه ها در اولین آزمایش با موفقیت ساخته شوند. صرف نظر از سیستم مورد استفاده و مستقل از بستری که هدف قرار گرفته است.
Zig پیشنهاد می کند که سیستم ساخت یک پروژه را با یک زبان منطقی با استفاده از API اعلاناتی برای ساخت پروژه ها جایگزین کند، که مدیریت بسته ها را نیز فراهم می کند، و بنابراین توانایی وابستگی واقعی به دیگر کتابخانه های C را دارد. توانایی داشتن وابستگی ها، انتزاعات سطح بالاتری را امکان پذیر می کند، و در نتیجه تکثیر کد سطح بالا قابل استفاده مجدد.
سادگی
C++، Rust و D دارای ویژگی های بسیار زیادی هستند که می تواند توجه را از معنای واقعی برنامه ای را که بر روی آن کار می کنید، منحرف کند. شخص به جای اشکال زدایی خود برنامه، خود را در حال اشکال زدایی از زبان برنامه نویسی می یابد.
Zig فاقد ماکرو و metaprogramming است، اما هنوز به اندازه کافی قدرتمند است تا برنامه های پیچیده را به روشنی و بدون تکرار بیان کند. حتی Rust که دارای فرمت موارد خاص ماکرو است، آن را در خود کامپایلر پیاده سازی می کند. در همین حال در Zig، تابع معادل در کتابخانه استاندارد بدون کد مورد خاصی در کامپایلر پیاده سازی می شود.
ابزار سازی
Zig را می توانید از بخش بارگیری ها بارگیری کنید. Zig باینری های آماده برای لینوکس، ویندوز، macOS و FreeBSD ارائه می دهد. موارد زیر آنچه را که با یکی از این آرشیوها به دست می آورید شرح می دهد:
- با بارگیری و استخراج یک آرشیو واحد، بدون نیاز به پیکربندی سیستم، Zig نصب شده است
- در صورت ایستا گردآوری شده است تا هیچ وابستگی به runtime وجود نداشته باشد
- از زیرساخت LLVM با پشتیبانی خوب استفاده می کند که بهینه سازی عمیق و پشتیبانی برای اکثر سیستم عامل های اصلی را امکان پذیر می کند
- کراس کامپایلینگ برای تمام پلتفرم های معروف
- با کد مبدا برای libc که به صورت پویا در زمان مورد نیاز برای هر پلت فرم پشتیبانی شده جمعآوری خواهد شد.
- شامل سیستم ساخت با ذخیره سازی است
- کد C و C++ را با پشتیبانی libc کامپایل می کند