Rust for Python developers
1.85K subscribers
21 photos
1 video
2 files
71 links
Rust programming language for python developers

یک توسعه دهنده پایتون هستم که سعی میکنم rust یاد بگیرم.
تو این مسیر منابع و نظرات شخصی خودم رو با آیندگان هم به اشتراک میذارم

اگر به هوش مصنوعی و پایتون علاقه دارید به کانال :
@pytens
@pyhints
سر بزنید.
Download Telegram
در ادامه تمرین‌های Rust رو آوردم به Dioxus چرا ؟
فقط چون با Rust میشه فرانت‌اند وب زد

البته توی داکیومنتش گفته که از Tauri هم استفاده می‌کنند برای desktop build های مختلفی که دارند و شاید بهتر باشه منم برم سراغ همین تا Tauri (فعلا روی بکند و tui هستم)

https://dioxuslabs.com
👍15
هفته پیش اولین پروژه Rust خودم رو تحویل دادم.

یک ابزار تحت ترمینال برای آنالیز فایل (تعداد خیلی بالا) بود.
توی پایتون نوشته شده بود، می‌خواستند بیارنش روی Rust و یکسری ویژگی جدید هم بهش اضافه کنند.
هرچند من بعد از خوندن کدها بهشون اطلاع دادم که کد پایتون درست نوشته نشده و همون باعث کند بودن هست و می‌تونم درستش کنم ولی به خواست خودشون رفتم روی Rust امروز تأیید خروجی و تست‌ها و پرداخت انجام شد و شد اولین دستمزد Rust من و سریعترین درآمد مبلغ خوبم از یادگیری زبان برنامه‌نویسی جدید.

البته این درآمد نشه موضوعی برای اینکه برید Rust یادبگیرید، چون قطعاً تجربیات دیگری که داشتم باعث شد این پروژه رو بگیرم.

اما، خواستم این رو بگم که بیشترین کمک رو بهم، خوندن سورس کد دیگران کرد.
مثلاً پروژه Limbo که بالاتر گفتم، خیلی خیلی کمکم کرد که توی ۱ روز تمام دستورات cli پروژه رو بزنم.

ازم درخواست tui کردند که گفتم درحال حاضر وقتش رو ندارم و حقیقتا تا حالا هم tui برای Rust نخوندم.
احتمالاً بعد از خوندن ۲-۳ تا سورس کد دیگری که در دستم هست و تمرین کردنشون برم سراغ یادگیری پکیج‌های tui و بعد از اون Tauri یا Dioxus ولی این موضوع انگیزه شد مضاعف برای ادامه مسیر.
👍3910👏4🔥3❤‍🔥2🥱1
یک موضوعی از شب قبل رفته توی مغزم
#5min_Rust

بعد پست بالا توی کانال، Intec یکی از بچه‌ها چندتا سوال Rust داشت که تونستم جواب بدم.

وقتی متوجه شدند این کانال وجود داره، پیشنهاد دادند که توی این کانال روزانه یا ... پستهای آموزش Rust بذارم.

از مباحث ساده شروع کنیم.

حقیقتا پیشنهاد خوبی بود (اگر بتونم خوب هم انجامش بدم) متن کوتاه باشه که از حوصله کسی خارج نشه.

و شدنی هم هست، چون برخلاف کانال پایتون که هدف تخصصی صحبت کردن بود.
اینجا هدف شروع مقدمات هست.

نمی‌دونم نهایتاً چی میشه ولی تلاشم رو می‌کنم، امیدوارم چیز خوبی بشه ولی اگر نشد شما بهم بگید 🌹
👏5021🔥6👍5❤‍🔥1
Rust for Python developers
یک موضوعی از شب قبل رفته توی مغزم #5min_Rust بعد پست بالا توی کانال، Intec یکی از بچه‌ها چندتا سوال Rust داشت که تونستم جواب بدم. وقتی متوجه شدند این کانال وجود داره، پیشنهاد دادند که توی این کانال روزانه یا ... پستهای آموزش Rust بذارم. از مباحث ساده…
از امشب شروع کنیم

بخش‌های اول که ساده‌تر هست رو سعی می‌کنم بیشتر بگم (که سریع ازشون بگذریم)

توقع این هست که همگی، با یک زبان برنامه‌نویسی کار کرده باشید توجه کنید صرف اینکه با اون زبان کد زدید اینجا مناسب نیست باید زبان رو درک کرده باشید.

مفاهیم software enginnering رو توی زبان برنامه‌نویسی که باهاش تجربه دارید بخوبی بلد باشید.

اگر این ویژگی رو ندارید، یا Rust اولین زبان برنامه‌نویسی هست که یاد می‌گرید؛ نمی‌گم شدنی نیست ولی قراره روزای سختی رو داشته باشید پس نیاز به انگیزه قوی داره.

نکته آخر: اگر Rust رو برای کسب درآمد و سرکار رفتن می‌خواید، زبان و گزینه خوبی نیست حداقل درحال حاضر در ایران و اروپا.


فکر نکنم نکته دیگه‌ای لازم باشه، همه می‌تونند Rust رو یاد بگیرند.
38👏6🔥4👍3❤‍🔥1
ازونجایی که بکگراند خیلی‌ها توی کانال python هست ترجیح میدم با چرایی Rust شروع کنم (هرچند برای خودم دلیلی نداشت؛ نشستم کد زدم و لذت بردم و به اینکار ادامه دادم.)

خیلی از پست‌ها ممکنه شوخی توش باشه (خودم اینطوری یادم می‌مونه) و اینکه ممکنه ۱۰۰٪ درست نباشه متن اما برای ساده‌سازی و درک بهتر تصمیم گرفتم از یکسری چیزا بگذرم؛ هرچی پیشرفته تر بشیم بر میگردیم به عقب و اون موارد رو هم تصحیح می‌کنیم.

#5min_Rust

اصن چرا Rust ؟ چرا Chap نه ؟
اسمش از یک قارچ اومده که عملکرد خیلی باحالی داره (بخونید راجبش)

دو مدل مدیریت حافظه داریم:
یا دست خود برنامه‌نویس می‌سپارید و میگیم برو به امان خدا.
یا آشغال جمع‌کنی (garbage collector) می‌ذاریم و می‌گیم اعتمادی به برنامه‌نویس نیست.

حالت اول میشه زبان‌هایی مثل C/C++ و بزرگترین مشکل کدهاش اینه که ی آدرس از یک خونه حافظه داریم که داره به یک جایی اشاره می‌کنه؛ حالا اون نقطه از خونه حافظه رو توی یک بخش دیگه کدهامون اومدیم خالی کردیم (دیتایی نداره).

فرض کن من یک خونه حافظه گرفتم و توی اون حاصل یک سری محاسبات رو بصورت String ذخیره کردم و یک اشاره‌گر به اون خونه حاقظه رو توی کدم دارم (آدرسش رو گرفتم بعدا با بچه‌ها مزاحم بشیم)؛ یک جای دیگه کدهام توی یک تابع دیگه بعدها اومدم اون خونه حاقظه رو گرفتم و یک سری کار باهاش کردم و دوباره ذخیره کردم همونجا؛ بیاید فرض کنیم این کارا که کردم یک پسورد رو براساس String اولیه ساخته.

اون آدرس رو اگر یادم رفته باشه آپدیتش کنم یا ... و هنوز به این خونه حافظه دسترسی داشته باشه اون وقت به فنا رفتم؛ همونطور که دوس ندارید لخت دوس دختر/دوس پسرتون رو کسی ببینه؛ پسورد لختتون رو هم نمی‌خواید با رفقا به خونه حافظه‌اش سر بزنید.

پس یک مشکل اساسی اینجاس (خیلی ساده گفتم کسی یادش نره)

حالت دوم زبان‌هایی مثل Python/Java می‌شه یکی هست به اسم آشغال جمع کن که وقتی خونه حافظه قابل دسترسی نیست یا رفرنس دیگه استفاده اولیه‌اش رو نداره اینارو حذف می‌کنه.
خب مشکلمون حل شد بریم خونمون ؟

مسئله اینجاس که دست دوم زبان‌ها سربار دارند و بدرد کارهای Realtime, Mission Critical نمی‌خورند دسته اول هم که بدرد میخوره وقتی پروژه گنده میشه تا دلتون بخواد باگ می‌کوبه تو صورت دولوپرها.

اما زبان Rust اومد با یک ایده دیگه (با پیاده سازی درست) :
گفت من یک کاری انجام میدم که Garbage Collector نمی‌خوام ولی مموری رو امن میدم در اختیارت که بری عشق کنی.
همه چیز اینجا صاحاب داره و تا صاحابش اجازه نده؛ نگاشم نمی‌تونی بکنی.
همین مورد چندین مشکل رو حل کرد:
۱- دستبرسی به خونه خالی نداریم: null dereferencing
۲- هیچ اشاره‌گری رو معلق نمی‌ذاریم بریم: dangling pointer
۳- اصراف هم نمی‌کنیم چون کار خوبی نیست: buffer overflow

حل همین ۳ تا مشکل باعث شد Rust کلی مرید پیدا کنه. ولی مسئله سرعت اجرا چی ‌میشه ؟ Rust اومد گفت بذارید دولوپر اذیت بشه ولی کاربر نشه چون شما یک کد رو ۱ بار می‌نویسی ولی ممکنه میلیون‌ها بار اجرا بشه پس همه بررسی‌های مربوط به قوانین همه چیز صاحاب داره؛ بی‌صاحابی در زمان کامپایل بررسی میشه.

یعنی چیزی دستت رو نمی‌گیره مگر اینکه قوانین رو رعایت کرده باشی (حالا جلوتر می‌بینیم خودش چقدر خوب مشکلاتت رو بهت می‌گه) چی شد که مریداش سر به بیابان گذاشتن (بله گونه‌های اولیه خشتک به دهان.) هیچی Rust گفت همه هزینه‌هاتون با من شما پرفورمنس C بهت تحویل داده می‌شه.
👍45🔥106👏4😁3❤‍🔥2💩2
#5min_Rust

برای نصب rust روی سیستم؛ به rust-lang سر بزنید اگر از لینوکس استفاده می‌کنید ۱ خط دستور رو توی ترمینال می‌زنید؛ اگر اینترنت ضعیف باشه ی چایی بریزی برگردی نصب شده.

اگر روی vs-code کد میزنید؛ بعدش حتما rust-analyzer رو نصب کنید. اگر روی Pycharm کد میزنید که برید یک محصول دیگه رو هم بخرید 😂
اگر روی Neovim هستید هم که؛ چایی دم کردم ناراحت می‌شم جای دیگه برید یک سری به github خودم بزنید.

نصب کنید که از پست بعدی کد هم بزنید.
👍318🤣6👌4❤‍🔥2
#5min_Rust

بعد از نصب rust برای ایجاد اولین پروژه از cargo استفاده می‌کنیم. با دستور cargo new pyrust شما یک پوشه جدید توی مسیری که این دستور رو اجرا کردید خواهید داشت به اسم pyrust.

تمام پروژه‌هایی که با cargo ایجاد می‌شوند حتما اسکلت مشابه دارند (مثال زیر)
.
├── Cargo.toml
├── .git
├── .gitignore
└── src
└── main.rs


قبل از بررسی فایل‌ها باید بگم که؛ این یک پروژه باینری هست (درآینده بررسی خواهیم کرد ولی اگر کتابخونه بود بجای main.rs شما lib.rs می‌دیدید)

تمام پروژه‌هایی که با cargo ایجاد می‌شوند بصورت خودکار git, gitignore رو خواهند داشت بریم باقی رو بررسی کنیم.

1) Cargo.toml
[package]
name = "pyrust"
version = "0.1.0"
edition = "2021"

[dependencies]

این فایل شامل اطلاعات پروژه هست؛ بخش dependencies بخشی هست که موقع استفاده از لایبراری‌های دیگه (ازین به بعد بهشون میگیم crate چون اسمش توی Rust این هست) باید پر کنید تا cargo اونهارو دانلود و استفاده کنه.

یک نکته دیگه که کمتر دقت میشه بهش؛ edition هست با این معنی که این پکیج باینری تا نسخه‌ای که سال 2021 منتشر شد backward compatibility رو داره.

شما ۳ تا ادیشن رو ممکنه ببینید :
۲۰۱۵ نسخه‌ای که Rust منتشر شد.
۲۰۱۸ نسخه‌ای که module, async/await و ... منتشر شد.
۲۰۲۰ نسخه‌ای که استانداردهای خیلی خوبی برای closure, iterate, ... منتشر شده.

همه چیز در Rust از src/main.rs و تابعی به اسم main شروع می‌شه؛ پس اگر پکیج باینتری داشته باشید حتما باید این موارد رو هم داشته باشید :

2) main.rs
fn main() {
println!("Hello, world!");
}


بصورت پیشفرض داخلی این فایل hello world رو خواهید داشت؛ fn کلمه کلیدی برای تعریف تابع هست main اسم تابع که هیچ آرگومانی بعنوان ورودی نمیگیره و هیچ چیزی هم بر نمی‌گردونه یا اصطلاحا unit type برمیگرده به () میگیم unit type و وقتی یک تابعی () بر میگردونه دیگه نیازی نیست اون رو بنویسیم اما اگر توی کد بالا می‌خواستیم بگیم که تابع main فقط unit type برمی‌گردونه کد به اینصورت م+یشد :
fn main() -> () {...}

از {} ها برای نمایش اسکوپ تابع (از کجا تا کجای کد مال این تابع هست) استفاده میشه همونطوری که توی پایتون برای اینکار از indentation استفاده می‌شه.
داخل این تابع فقط قرار هست یک متنی رو توی ترمینال چاپ کنیم؛ برای چاپ متن داخل ترمینال از println استفاده میشه که بعد از چاپ متن میره خط بعدی اگر نخواستیم بره خط بعدی از print استفاده می‌کنیم.
println!(), print!()

اما اگر دقت کنید توی کد بالا هم print و هم println هردو کنار اسمشون ! اومده؛ این ! اشتباه نیست و توی Rust یک مفهومی رو معرفی می‌کنه به اسم Macro پس نه print و نه println هیچکدوم تابع نیستند.

خیلی خلاصه بخوام بگم؛ وقتی کامپایلر یک macro توی کد شما می‌بینه می‌دونه که باید اون بخش از کدشما رو برداره و بر اساس یک الگوی خاص کد دیگری رو با پارامتر ورودی شما جایگزینش کنه.
در آخر هم توجه شمارو به ; جلب می‌کنم.

قبل از اجرای کد؛ اگر از rust-analyzer استفاده می‌کنید حتما باید داخل پوشه‌ای باشید که Cargo.toml قرار داره.

❯ cargo run
Compiling pyrust v0.1.0 (/home/mo.abbasi/Desktop/codes/learning/rust/pyrust)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.64s
Running `target/debug/pyrust`
Hello, world!

رو می‌تونید برای اجرای پروژه استفاده کنید؛ اگر از cargo run برای اجرا استفاده کنید نسخه‌ای که کامپایل میشه نسخه development هست و برای اینکه سریعتر کامپایل انجام بشه unomptimized هست (که توی توضیحات هم چاپ میشه) اگر توی نسخه develop نخواستید این توضیحات رو ببینید از دستور cargo run -q استفاده کنید.

بعد از اولین اجرا یا به محض فعال شدن rust analyzer؛ ۱ فایل و ۱ پوشه جدید توی پروژه خواهید دید. Cargo.lock که هیچوقت بهش دست نمیزنیم و جزئیات dependency هارو نگه میداره و البته پوشه target که نسخه کامپایل رو نگه می‌داره چون پروژه رو تو حالت development/debug کامپایل کردیم الان می‌تونیم فایل باینتری - کامپایل شده رو از پوشه target هم ببینید و اجرا کنیم :
target/debug/pyrust


تبریک می‌گم شما با ساختار اصلی و اولیه پروژه باینری در Rust آشنا شدید.
29👍16🔥5
Forwarded from RandRng
با صحبت امروز پزشکیان
من روی دلار ۱۱۰,۰۰۰ تومان دارم می‌بندم.

آخر سال تمام قراردادهای همکاری داخلیم تموم می‌شه و دستمزد جدید رو بر این اساس می‌گیرم، البته که دارم فرض می‌کنم دلار تا چندروز آینده به ۱۰۰,۰۰۰ تومان برسه و مدتی همین حوالی باشه‌.

اگر دلار واقعاً به ۱۱۰,۰۰۰ تومان برسه (که اصلاً بعید نیست)
قراردادهای سال بعدی رو روی دلار ۱۳۰,۰۰۰ تومان می‌بندم.

به ۲ دلیل این رو میگم:

۱- بچه‌های سنیور گفتند چطوری دستمزد بدیم برای سال بعد.

۲- چند نفر دعوت به همکاری کردن، گفتم حدود دستمزد رو بدوند که وقت هم رو نگیریم.

دستمزد فعلی رو بر اساس دلار ۶۰,۰۰۰ تومان بسته بودم سال قبل که خدا بیامرزش.
👍23💔42
#5min_Rust

انواع روش‌های لیبل زدن برای مقادیر رو توی این پست بررسی کنیم؛ معمولا این رو با اسم متغییر می‌شناسیم ولی خب توی زبان‌های مثل Rust اسم متغییر کمی گیج کننده هست. چرا ؟

اول با immutable, mutable شروع کنیم (تغییر پذیر و تغییر ناپذیر) :
بصورت پیش‌فرض تمام متغییرهایی که توی Rust تعریف می‌کنیم تغییرناپذیر هستند و برای تعریف یک متغییر از کلید let استفاده می‌شه یک مثال ببینیم:
let channel = "pyrust";

اگر بصورت immutable یک متغییر رو تعریف کنید تا آخر نمی‌تونید مقدارش رو تغییر بدید؛ اما توجه کنید با اینکه زبان Rust زبان Static type هست من توی این مثال type متغییر رو تعریف نکردم (راجب type ها تصمیم گرفتم بعد این پست صحبت کنم چون آموزش ویدئویی نیست و برای مثال زدن روی اون موارد باید این پست خونده می‌شد) فعلا بیاید قبول کنیم که "pyrust" توی مثال بالا از نوع داده اصطلاحا string slice هست. خود کامپایلر انقدر باهوش هست که شما نیازی ندارید این type هارو بهش بگید و می‌تونه خودش حدس بزنه ولی اگر خواستید بصورت دستی بهش تایپ هم بدید :
let channel: &str = "pyrust";

از کلمه let برای تعریف متغییر استفاده می‌شه؛ بعد از اون اسم متغییر میاد که اینجا channel هست و بعد از اون : &str که معلوم می‌کنه این متغییر از چه نوع داده‌ای باید باشه و هست باقیشم که توی همه زبان‌ها یکسان هست.
اما اگر بخوایم یک متغییر تعریف کنیم و به کامپایلر بگیم در طول اجرای برنامه این متغییر اجازه داره مقدارش عوض بشه چطور ؟ فقط کافیه از کلمه کلیدی mut (مختصر شده mutable استفاده کنیم) :
let mut channel: &str = "pyrust";

تنها تفاوت اینه که قبل از اسم متغییر (channel) باید mut رو بذارید.
این موارد رو تست کنید:
fn main() {
let channel = "pyrust";
channel = "pyhints";
println!("Channel is: {}", channel);
}

اگر به ارور کامپایلر (یا rust-analyzer) دقت کنید؛ می‌بینید که علاوه بر اینکه ارور رو بهتون می‌گه راهکار رو هم بهتون نشون میده؛ تستش کنید.
fn main() {
let mut channel = "pyrust";
channel = "pyhints";
println!("Channel is: {}", channel);
}

توی مثال بالا مقداری warning می‌گیرید که اهمیتی نداره فعلا؛ اما حتی وقتی متغییر رو mut بکنید اجازه ندارید نوع داده داخلش رو عوض کنید:
fn main() {
let mut channel = "pyrust";
channel = 1590;
println!("Channel is: {}", channel);
}

این متغییر تا آخر باید از نوع string slice بمونه؛ البته با shadowing می‌شه از همین اسم مجدد استفاده کرد ولی اون موضوع برای آینده هست.

بعد از let به یک کلمه کلیدی دیگه میرسیم به اسم const مخفف constant برای تعریف مقادیر ثابت استفاده می‌شه و حتما هم باید بهشون type رو بدید اولین نکته درباره const در Rust اینه که مقدارش حتما باید قبلا از کامپایل بهش داده بشه.
این یعنی شما نمی‌تونی نتیجه محاسبات یا دیتای ورودی کاربر و ... رو توی const نگهدارید.

const OWNER: &str = "Mo.Abbasi";

fn main() {
let channel = "pyrust";
println!("Channel {channel} owner is {OWNER}");
}

توی این مثال چندتا نکته هست:
اول؛ const می‌تونه بعنوان global variable تعریف بشه؛ خارج از محدوده main. توی چندین scope جا میگیره تابع؛ ماژول و یا گلوبال (راجب scope هم مثال خواهم زد).
دوم؛ همیشه برای اسم constant ها از حروف بزرگ باید استفاده بشه.
سوم؛ println هست که می‌تونم اسم متغییر رو داخل {} بذارم؛ راحتر از موارد قبلی.

در نهایت آخرین مورد static هست؛ مثل const باید تایپ رو بهش بدید؛ مثل let هم mut, immut داره (که البته ۹۹٪ سعی کنید از mut اش استفاده نکنید.) مقدارش می‌تونه در زمان کامپایل یا در زمان initialize کردن برنامه مشخص بشه (مثلا config مربوط به اجرای برنامه‌)
خوبیش اینه که از شروع کار برنامه تا خروج ازش می‌تونید بهش دسترسی داشته باشید اصطلاحاْ liftime اش به اندازه خود پروسس کد اصلی هست.


توی تمام مثال‌های بالا بیشتر تفاوت و استفاده‌ها به lifetime, scope برمی‌گرده اما با همین توضیحات ساده هم شما باید بتونید تشخیص بدید چه زمانی از کدوم استفاده کنید.
نکته بعدی نحوه مصرف مموری هست؛ این رو فقط بعنوان رفرنس میذارم چون جلوتر بررسی خواهیم کرد (پست‌های آینده)

let: Stack/Heap depending on the data type.
const: Compiler inline / memory location.
static: Static memory region.
👍197👌1🎃1
Rust for Python developers
یک پروژه بهم داده شده که بعد از سال‌های بسیار (شاید ۸ سال) برای اولین بار پروژه رو از روز اولش هستم. کدها رو باید توی پایتون انجام بدم؛ معمار سیستم اینطوری فکر می‌کنه که پایتون توی اسکیل هم جواب میده برای این پروژه خاص. (قطعا جواب میده اما سخت هست یا سخت‌افزار…
نمیدونم چرا این کتاب رو نخوندم و ندیده بودم (سرفصل‌هاش رو)

بسیار کتاب جذاب و خوبی هست؛ بله درسته با Rust 2018 هست ولی واقعا مهم نیست.
متوجه شدم یکی از دوستانی که توی گوگل دارم؛ تغییر Stack داده از C به Rust ایشون توی تیم کروم بودند.
کلی باهاش صحبت کردم؛ چون توی این مدت بسیار به نوشتن کرنل علاقمند شدم و خودم دارم روش کار می‌کنم (کند پیش میرم چون تسک‌های دیگه دارم ولی پیش میرم.)
ازش خواستم توی این موضوع بهم کمک کنه منابعی که خوندم رو بهش دادم؛ چندتا سوال ازم پرسید (مثل مصاحبه) و بعد این منبع رو بهم داد:
Programming Rust 2nd Edition

خوشحال شدم که همین اول مسیر یکی مشکلاتم رو بهم گفت؛ ناراحت شدم ازینکه با این تجربه چرا این کتاب رو نخوندم؛ دلیلش رو هم می‌دونم.

خیلی سال هست توی دنیای پایتون - هوش مصنوعی بودم؛ توی اون دنیا سرعت تغییراتی که باقی چیزها رو خراب کنه خیلی زیاده (بیش از حد) برای همین منبع ۳ سال قدیمی خیلی به کار نمیاد تازه منابع یادگیری هم خیلی زیاد و راحت در دسترس هست؛ اشتباه کردم با اون دید توی دنیای Rust دنبال منبع گشتم.

حالا اولویتم این کتاب هست؛ تا شنبه بنظرم بتونم ۱۲-۱۳ فصل اول رو بخونم و تمرین کنم.
یا اصطلاح دید یک سری منابع دیگه هم پیدا کردم که زیر این پست میذارم اگر دوست داشتید بررسی کنید.

۱- این از یک دوره اومده که certificate می‌داده برای Rust بنظرم برای مصاحبه خوبه چون تمرینات خوب و سوال جوابای خوبی داره (با اصطلاحات به خوبی آشنا می‌شید)
RustCamp
۲- موارد خیلی مهم مربوط به trait های standard library رو توی این مورد پیدا می‌کنید؛ کسایی که با Rust کمی جدی کد زده باشند می‌دونند که خیلی وقتا باید این موارد رو بشناسی تا برای struct, enum, ... خودت تعریفشون کنی و زندگی رو راحت کنی
Tour of std

۳- از همون نویسنده قیلی این پستش هم خیلی خوبه؛ اشتباهات رایج توی درک Lifetime رو اینجا گفته
Lifetime Misconceptions

۴- از سایت تمرینات راست هم این مورد خیلی خوبه (من تمرین کردن رو از خوندن بیشتر دوس دارم) :
100 Exercises to learn Rust

۵- در نهایت اینم چون از دنیای پایتون اومدم:
Rust-Python interoperability

شخصا به ترتیب با اولویت کتاب توی وقتهای خالی دارم این مورد رو پیش می‌برم.
👍197
#5min_Rust

تفاوت Stack, Heap, Static در Rust:

اولین نکته اینه که خیلی از دوستان به اشتباه فکر می‌کنند که این ۳ مورد حافظه‌های متفاوتی هست و این مشکل از اینجا میاد که راجب سرعت صحبت می‌شه.

توی تصویر مثال بالا اگر دقت کنید؛ هر ۳ مورد داخل RAM هستند فقط ویژگی‌های مختلفی دارند که بهشون می‌پردازیم:
وقتی شما کد رو اجرا می‌کنید اول یک سری فضا به برخی موارد اختصاص داده می‌شه؛ برای مثال خود دستورالعمل‌های کد شما که توی تصویر سمت چپ Stack هستند؛ این بخش شامل static, global variable و ... هم میشه.
از مثال زدن data type ها پرهیز می‌کنم چون هنوز باهاشون آشنا نشدیم.
بعد از اینکار برای Rust یک فضای 8MB پشت سرهم درخواست داده می‌شه که این فضا بعنوان Stack اصلی توسط برنامه استفاده خواهد شد.
دیفالت 8mb هست برای ترد اصلی و 2mb برای تردهای دیگه توجه کنید که لزوما همون لحظه کل 8mb رزرو نخواهد شد اما برنامه شما تا 8mb دسترسی به استک داره و اگر بیشتر بشه stack overflow رخ میده و برنامه kill میشه.

با ویژگی‌های اصلی استک شروع کنیم و بعد به سراغ مثال بریم:
۱- سرعت؛ توی تصویر دقت کنید؛ یک بخشی بین stack, heap نوشتم Stack Pointer؛ یکی از رجیسترهای CPU وظیفه نگهداری آدرس شروع Stack رو به عهده می‌گیره و هموراه به آدرس انتهایی آخرین دیتای موجود در استک اشاره می‌کنه.

۲- هر نوع داده‌ای که می‌خواد داخل استک قرار بگیره باید سایز مشخصی داشته باشه. در زمان کامپایل باید مشخص بشه چقدر جا می‌خواد.

ترکیب دو مورد بالا باعث میشه که بتونیم خیلی سریع به دیتاهای روی Stack دسترسی بگیریم اما یک محدودیت هم هست؛ Stack مثل بشقاب چینی می‌مونه وقتی روی هم میچینی نمی‌‌تونی از آخر ی دونه رو بکشی بیرون؛ باید به ترتیب از بالاترین بشقاب برداری تا به پایینی (آخری برسی).

مورد بعدی Heap اما داستان متفاوتی داره؛ برخلاف Stack که خودش بصورت خودکار حافظه اختصاص میده و میگیره heap اینطوری نیست و هروقت به این حافظه نیاز داشته باشه باید به سیستم عامل بگه که یک همچین حافظه‌ای لازمه تا سیستم عامل اون میزان رو پیدا کنه و بهش بگه از اینجا به بعد رو می‌تونی استفاده کنی.

توی عکس بالا توی بخش heap خونه‌های قرمز بخش‌های از حافظه‌ هست که برای کارهای دیگه اختصاص داده شده و ما نمی‌تونیم دسترسی بگیریم؛ همین بررسی اینکه کجا رو به ما اختصاص بده باعث میشه سرعت این حافظه کندتر باشه.
پاک کردن داده از Heap هم توسط ownership, borrowing توی Rust مدیریت میشه که بعدا راجبش صحبت می‌کنیم.

توی پست بعدی راجب نمونه کد و جزئیاتش توی تصویر بالا صحبت خواهیم کرد.

پینوشت:
من سعی کردم خیلی ساده توضیح بدم تا کلیات و تفاوت‌های اصلی رو همه متوجه بشوند و از بحث راجب نحوه دقیق عملکرد در اینجا خودداری کردم (باشه برای آینده)
👍136
Rust for Python developers
#5min_Rust تفاوت Stack, Heap, Static در Rust: اولین نکته اینه که خیلی از دوستان به اشتباه فکر می‌کنند که این ۳ مورد حافظه‌های متفاوتی هست و این مشکل از اینجا میاد که راجب سرعت صحبت می‌شه. توی تصویر مثال بالا اگر دقت کنید؛ هر ۳ مورد داخل RAM هستند فقط ویژگی‌های…
stack_vs_heap.png
166 KB
#5min_Rust

خب توی این مثال؛ اول از همه یک مقدار حافظه از Stack به تابع main اختصاص داده می‌شه؛ توی اولین دستور داخل main یک متغییر داریم به اسم a و مقدار 22 که داخل Stack قرار می‌گیره (نوع داده int چون سایزش زمان کامپایل مشخص هست همیشه داخل stack قرار میگیره)

بعد از اون برای بدست آوردن مقدار b باید تابع دیگری صدا زده بشه؛ که اینبار add_one هست و یک فضای اختصاصی روی Stack بهش داده می‌شه؛ و کاری که می‌کنه اینه که ورودی رو +1 می‌کنه و برای کسی که صداش زده برمی‌گردونه پس یک متغییر به اسم i داره که آرگومان ورودی تابع هست و توی استک قرار میگیره و خروجی هم توسط return برای آدرس b توی main ارسال میشه 0x23f توی این مثال؛ توی این بازه که داشتیم روی add_one کار میکردیم پشت صحنه SP هم جابجا شد و بجای اینکه به آخر main روی stack اشاره کنه به انتهای آدرس add_one اشاره میکرد.
وقتی کارمون با تابع add_one تموم شد و مقدارش رو برگردوندیم؛ این بخش از Stack حذف میشه؛ باتمام متغییرها و مقادیری که توی این بخش بود (درک این موضوع به lifetime, ownership, ... کمک می‌کنه پس یادتون بمونه)
و SP دوباره بر میگرده و انتهای main رو نشون میده و b 23 داخل استک main قرار میگیره.

بعد از اون متغییر answer_universe رو داریم؛ این مقدار رو چون می‌خواستیم بمونه و با حذف Stack پاک نشه تصمیم گرفتیم بفرستیمش روی Heap اما یادتون باشه بالاتر گفتم int روی stack جا داره چون سایزش از قبل معلوم هست؛ برای اینکه به زور ببریمش روی Heap از چیزی به اسم Box استفاده می‌کنیم (درآینده راجبش حرف میزنم)

وقتی on_heap صدا زده میشه؛ استک فقط و فقط شامل main هست و add_one حذف شده ازش (توی تصویر نمی‌شد این رو نشون داد) on_heap داخل خودش b رو داره (آپدیت کردن SP یادمون نرفته فقط دوباره توضحیش نمیدم) و b رو میخوایم روی heap بفرستیم پس به سیستم درخواست میدیم یک فضایی به اندازه i32 نوع داده integer 32bit برامون روی heap پیدا کنه و بهمون بده وقتی سیستم این رو پیدا کرد دیتای 42 رو اونجا مینوسه و آدرسش 0x5f21 رو بهمون بر میگردونه و بعد هم که return , ....

بعد از این مرحله تابع on_heap هم از روی stack حذف میشه و فقط main ‌می‌مونه روی main چون answer_universe هنوز به دیتای 42 روی heap نیاز داره پس اون دیتاهم روی heap وجود داره.

در نهایت وقتی این کارها تموم شد (اینجا print, ... نداریم) برنامه بطور کامل اجرا شده و main هم تموم می‌شه و تمام مموری پاک میشه.


پینوشت:
منبع نمونه کد بالا؛ این رو قبلا گذاشته بودم بنظرم خوبه دوباره زیر این پست هم باشه.
👍115
Rust for Python developers
stack_vs_heap.png
#5min_Rust

درنهایت نکات مهمی که راجب Stack باید یادتون بمونه :

۱- سرعت بالاتری داره نسبت به heap؛ چون برای دیتاهاش نیازی نداره به سیستم عامل بگه براش حافظه پیدا کنه ( system call کمتری داره )
۲- داده‌هایی می‌تونند روی Stack قرار بگیرند که از قبل سایزشون مشخص باشه؛ یعنی بدونیم چقدر فضای حافظه رو نیاز دارند.
۳- نمی‌تونیم از یک تابع به داده‌ای داخل تابع دیگر که روی استک هست اشاره کنیم؛ چون همونطور که دیدیم وقتی اجرا اون بخش کد تموم بشه تمام مقادیر از Stack حذف میشه و ما می‌مونیم و اشاره‌گر به خانه حافظه‌ای که یا خالی هست یا نباید بهش اشاره می‌شده و این موضوع امن نیست.
👍176
Rust 1.85.0:

بهترین چیزی که اضافه شده بنظرم؛ async closure هست.

let mut vec: Vec<String> = vec![];

let closure = async || {
vec.push(ready(String::from("")).await);
};


این موضوع خیلی کار رو نسبت به async block ها راحت‌تر می‌کنه دیگه درگیری ownership و ... رو نداره.

Rust edition 2024
هم همزمان منتشر شده؛ که یک سری رزرو جدید و ... داشته

Rust Blog

نظرشخصی:
بنظرم هرچی بیشتر جلو میریم کد زدن توی Rust راحت‌تر و تمیزتر خواهد شد.
👍164🤣1
توی ۷-۸ روز اخیر پروژه لینوکس کرنل یک مینتینرهایی رو داشت از دست میداد که نباید (لاشخورها هم نشسته بودن رو هوا بزنند‌ها؛ ویندوز و مک) بگذریم. کار به جایی رسید که اومدن برای این موضوع قانون گذاری کردند.
https://rust-for-linux.com/

مثل سری قبلی نظرات شخصی هم داشت وارد می‌شد؛ که بعضی مینتینرها داشتن می‌کفتند نمی‌خواند کد Rust ببینند و Accept کنند چون ممکنه باعث باگ بشه؛ توسعه دهنده‌های Rust که توی برخی موارد مینتینر بخش‌های دیگری از کرنل هم هستند با حفظ سمت داشتن می‌کفتند که بابا ما این کد Rust رو برای شما زدیم چون باگهای مموری شما مارو سرویس کرده و همین بحث که ما اضاقه نمی‌کنیم چون ممکنه باعث باگ بشه؛ اوناهم که خب چون باگ داری و توان فیکس ندارید ما کد دونیت کردیم و این شده بود یک loop تا قانون اضافه شد.

الان مشخص شده همین قانون هم خودش یک سلسله ایمیلی بوده (راستی همه‌ی ایمیل‌های بحث‌های توسعه کرنل لینوکس بطور کامل روی kernel.org هست؛ بعله حتی فحش ناموسی‌هایی که به دولوپر تازه‌کارا سر اشتباهاتشون دادن؛ اکثرا هم کار خود لینوس هستا؛ حالا ما اینجا به دولوپر میگیم بیشتر دقت کن گریه می‌کنه میره خونشون یا با اولیاش میاد)

داستان اینه لینوس شخصا می‌دونه که Rust باید بیاد توی کرنل چون باعث پیشرفت می‌شه و از رقیبا عقب نمیوفته ولی بعضی از مینتینرهای قدیمی که نمی‌تونند Rust رو یاد بگیرند دارند احمقانه باهاش مبارزه می‌کنند. (از حق نگذریم حدود ۱۰٪ هم حق دارند و منطقی توضیح می‌دهند که باید توی فلان بخش فعلا روی C بمونیم)؛ یک گروه دیگه هم هستند منطقی‌های C بلد که میگن کدهای اصلی که روی C نوشته شده بذاریم باشه (بالای ۳۰ میلیون خط کد هست کرنل) ولی کدهای جدید و ... رو باید بریم روی Rust اگر کسی توی دنیای Rust گردن نگرفت با C میزنیم و مثال هایی هم هست که توسعه Rust هفته‌ها جلوتر از C بوده مخصوصا برای سخت‌افزارهای جدید چون باگ کمتر داشته و کد زدن توی Rust برای سخت‌افزار به مراتب سریعتر از C هست؛ و خب بنظرم صحبت‌های این دسته ۱۰۰٪ منطقی هست ولی با همینم مخالفت می‌کنند.

دسته دیگری هم هستن که با دیتا صحبت می‌کنند؛ که بیشترین مشکل ما توی باگ‌هایی که باعث نفوذ به سرور شده توی 15+ سال قبل همش مربوط به مدیریت مموری توی C و خطای دولوپر بوده (اینا بهترین دلوپرهای دنیا هستنا) پس منطقی هست که سعی کنیم بریم سراغ Rust بدون شک و تردید در آینده باید این اتفاق بیوفته اینا با اینکه توسعه دهنده Rust نیستند ولی به اندازه تیم توسعه لینوکس کرنل با Rust موافق اضافه شدن Rust به کرنل هستند و کامل حمایت می‌کنند.

دعوا شدیدا بالا گرفته و نظرات شخصی خیلی خیلی داره روی کرنل لینوکس و البته آینده کاربرهاش تاثیر میذاره و بازم من با سخنرانی حذف شده یکی از maintainer های قبلی اشاره می‌کنم که این مزمون رو داشت (بعد از۳۵ دقیقه کل‌کل توی سخنرانیش) :

توی تیم Kernel فسیل‌های احمق و خودخواهی هستند که چون شعور و قدرت یادگیری زبان جدید (Rust) رو ندارند حاضرند این دستاورد (منظور پیشرفت‌های لینوکس بعد از سال‌ها و ورود بیشترش به دنیای دسکتاپ هست) رو با خودشون به نابودی ببرند.

این توی یکی از سخنرانی‌ها بود؛ موج اول خدافظی از Linux Rust Kernel رو بهمراه داشت؛ سخنرانی بعد ازین بحث تموم شد و ویدئو این بخش هم از یوتیوبشون حذف شد (اون روز بحث کردیم راجبش).

فعلا شخص لینوس تروالدز وارد شده و بنظر می‌رسه خودش موضوعات مربوط به Rust رو گردن بگیره که بسیار بسیار خوشحال کننده هست ولی کاش زودتر بود.

پینوشت:

کدی که سرش این دعوا اخیر بوجود اومد تایید شده و maintainer مخالف از این بخش (کل نه‌ها فقط همین بخش) حذف شد؛ دلیلشم این بود که مخالفتش غیر منطقی بوده (خود لینوس تروالدز این کدپ رو تایید کرده)
👍25🤣2
داشتم راجب linux io_uring میخوندم؛ که برام سوال شد آیا Rust هم ازش استفاده می‌کنه ؟
دیدم توکیو همه‌کاره اینکار رو هم می‌کنه؛ اما نه کاملا بهینه ولی bytedance شرکت مادر Tiktok یک پروژه داره به اسم monoio که کاملا هم فعال و آپدیت بگیر هست و بدون سربار داره از io_uring استفاده می‌کنه.

من روی تمرین Socket توی Rust به این موضوع رسیدم و دارم بیشتر باهاش آشنا میشم اما خوشحال میشم اگر کسی تجربه کار با این ابزار یا آشنایی دقیقی با io_uring داره یک پست؛ ویدئو یا ... مارو مهمون کنه توی کامنت‌ها.
👍122😁1
ماکروسافت دیروز اعلام کرد که کامپایلر Typescript رو منتقل کرده روی Go (قبلا روی خود TypeScript بود)

یک دلیلی که من مخالف ترجمه فارسی خیلی از مباحث هستم همین موضوع هست؛ ماکروسافت نگفته کامپایلر رو بازنویسی می‌کنه بلکه دقیقا تاکید روی Port کردن داشته.
یعنی همون کدهای قبل رو کپی پیست می‌کنند توی یک فایل جدید و مطمئن میشن سینتکس و ... به Go تبدیل بشه. (واقعا فارسی توضیح دادن تفاوت rewrite, port کردن خیلی سخته)

بگذریم؛ توی این بحث طرفدارای Go و البته اونایی که درک درستی از موضوع نداشتند خواستند از آب گل‌الود ماهی بگیرند که دوره‌های Go خودشون رو بفروشند یا اینکه نفرت پراکنی کنند راجب Rust ؛ اول اینکه اگر بیشتر رصد کنید خیلی‌ها کمی ناامید شدند که چرا Rust انتخاب نشد.

مثلا نویسنده Vue که توی توییت‌های مختلف توضیح داده که Go برای بعضی کاربردها مثل web assembly مناسب نیست و ...

اما بعنوان کسی که علاقه بسیاری به Rust دارم بنظرم تیم TypeScript تصمیم خوبی گرفته؛ اگر قرار به بازنویسی کامل TypeScript بود قطعا Rust بهترین گزینه می‌شد و با توجه به اینکه تیم کرنل ویندوز هم داره خیلی از لایبراری‌های اصلی رو میبره روی Rust نشون میده مشکلی هم با این قضیه نیست.

اما تیم TypeScript نمی‌خواد مجدد همه چیز رو از بیخ بازنویسی کنه و فقط داره Port می‌کنه تا زمان کامپایل رو کاهش بده؛ برای همین نیازی به سختی‌های Rust, C نداره و البته همین Port کردن کم هزینه هم حداقل بهش 10x سرعت داده.

دلیل انتخاب Golang بجای Rust, C فقط این موضوع بوده؛ خواستم شفاف بشیم روش.
👍34🤣1
#5min_Rust

قبل از اینکه دوپایی بپریم توی معرفی انواع Data type توی Rust یک تفاوت رو بین Rust, Python ببینیم.
توی rust بر خلاف python برنامه‌نویس نمی‌تونه هر زمانی که دلش خواست توی متغییر مقداری با دیتا تابپ متفاوت بریزه؛ کد زیر توی پایتون درست هست:
my_var  = 5
print(f"The content is {my_var} of type {type(my_var).__name__}")
my_var = "@pyrust"
print(f"The content is {my_var} of type {type(my_var).__name__}")

اما توی rust شما اجازه اینکار رو ندارید
fn main() {
let my_var: i32 = 5;
println!("The content is {my_var} of type {}", std::any::type_name::<i32>());

my_var = "Hello"; // ٍError: expected `i32`, found `&str`
}


پس توی rust خیلی مهم هست که مشخص کنید متغیر شما از چه نوعی باید باشه؛‌ البته توی خیلی موارد نیازی به نوشتن نداره و خود کامپایلر می‌تونه بر اساس استفاده‌ای که از متغییر می‌کنید (اولین نوع داده‌ای‌ که داخلش می‌ریزید) تصمیم بگیره.

پس با این حساب یادگیری Data Type مهم میشه و اولین نوع داده‌ای که بررسی می‌کنیم Integer خواهد بود.
بازم بر خلاف پایتون نوع int توی rust خودش چندین مدل داره؛ بصورت پیش‌فرض اگر هیچ موردی رو انتخاب نکنید کامپایلر برای شما i32 رو درنظر می‌گیره ولی این یعنی چی ؟
i32 : i = Signed, 32 = 32-bit value

اگر نوع int شما با i شروع شده بود یعنی Signed هست و این به این معنی هست که این نوع داده شامل اعداد صحیح مثبت و منفی می‌شه.
عدد 32 نشان دهنده تعداد بیت حافظه هست و محدوده اعدادی که این مقدار می‌تونه نگهداره رو شامل میشه.

برای راحتی من با i8, u8 مثال میزم:
اینجا u به معنی unsigned هست و یعنی این نوع داده اعداد منفی قبول نمی‌کنه؛ برای اینکه بفهمیم یک نوع داده چه رنج اعدادی رو داخل خودش نگهداری می‌کنه می‌تونیم از فرمول زیر استفاده کنیم:

i8 : Signed, 8bit
برای بدست آوردن رنج signed کافیه تعداد bit رو داخل این فرمول بذارید :
[-2^(n-1), (2^(n-1))-1] ==> [-2^7, (2^7)-1) ==> [-128, 127]

u8
برای بدست آوردن رنج unsigned ها کافیه تعداد bit رو داخل این فرمول بذارید:
(2^n)-1 ==> (2^8)-1 ==> 255


علامت ^ نماد توان هست.

اگر با اعداد باینری کمی آشنایی داشته باشید نیازی به حفظ کردن فرمول‌ها نیست؛ توی نوع unsigned ازونجایی که عدد مثبت و منفی رو قبول می‌کنه پس ۱ بیت رو از دست میده برای اینکه مشخص کنه عدد منفی هست یا مثبت پس این یعنی بخش توان فرمول -1 رو خواهد داشت فقط برای unsigned ها
اما توی هر دوحالت حاصل محسابات و به توان رسوندن همیشه -1 رو توی بخش مثبت هم داره؛ خیلی ساده دلیلش اینه که بزرگترین عدد مثبتی رو وقتی می‌تونید بسازید که همه‌ی بیت‌های باینری 1 باشند و هروقت بیت اول (از راست) مقدار 1 بگیره حاصل حتما عددی فرد خواهد بود.

بر همین اساس شما همه‌ی آنچه برای درک جدول تصویر پیوست لازمه رو می‌دونید؛ فقط یک مورد می‌مونه :

arch | isize | usize


که یعنی این نوع به معماری سیستم بستگی داره (معماری سیستم روی index گذاری برای دسترسی به خانه‌های RAM مهم هست) برای همین هرجا صحبت از دسترسی به حافظه یا index , ... باشه این نوع داده رو خواهید دید. اگر سیستم ۳۲ بیتی باشه مقدار isize = i32, usize = u32 خواهند بود و برای سیستم‌های ۶۴ بیتی مقادیر isize=i64, usize=u64 رو خواهیم داشت.

بعد از اون نوع داده float رو داریم؛ شامل f32, f64 تفاوتشون توی precision هست (تعداد اعدادی که بعد از اعشار میاد)

f32 precision: 6-9 digits
f64 precision: 15-17 digits

یعنی برای نوع داده f32 بعد از ۶-۹ رقم اعشار دقت رو از دست می‌دیم و حاصل محاسبات بعد از ۶-۹ رقم اعشار رند شده خواهد بود؛ برای f64 این مقدار به ۱۵-۱۷ میرسه؛ بصورت دیفالت کامپایلر متغیرها رو f64 درنظر میگیره مگر اینکه شما مشخص کنید نوع داده چیز دیگری باشه.

اگر رند کردن براتون قابل قبول نیست Decimal رو باید در نظر بگیرید که خب توی این مرحله راجبش صحبت نمی‌کنیم.


برای تعیین نوع داده هم بعد از ساخت متغییر و قبل از مقدار دهی می‌تونید نوع داده رو مشخص کنید:
let red_pixel: u8 = 25;
let my_negative_number: i32 = -5654891;
let pi: f64 = 3.14


بیشترین سوالی که پرسیده میشه؛ آیا باید همیشه برای پرفورمنس بیشتر کوچکترین نوع داده رو انتخاب کنیم ؟
خیر.
اولین و مهمترین موضوع؛ اجرا شدن درست برنامه هست؛ اما بعضی وقتا کاملا میشه مطمئن بود نوع داده‌ای باید چه مقداری باشه.
مثلا فرض کنید ما دارید یک برنامه برای پردازش تصاویر jpg می‌نویسید؛ توی این تصاویر همیشه مقدار هر پیکسل بین 0-255 خواهد بود و این یعنی شما برای متغییرهای کار با پیکسل می‌تونید با خیالت راحت نوع داده رو روی u8 تعریف کنید.
اما برای مواردی که مطمئن نیستید i32 شروع خوبی هست که پیش‌فرض کامپایلر هم همین مقدار هست.
👍203