راه‌های «اشتباه» ساخت String در Rust؛ از نام thread تا panic

چرا مهم است

اگر با String در Rust کار می‌کنی، این مثال‌ها تفاوت تبدیل byte به character و decode کردن UTF-8 را نشان می‌دهند. همچنین می‌بینی چرا خروجی Debug یا type_name تضمینی برای متن پایدار نیست.

قبل از خواندن، این‌ها را بدان۳ مفهوم
Serialization

تبدیل داده به قالبی که بتوان آن را ذخیره یا منتقل کرد و بعد دوباره خواند؛ خروجی مخصوص Debug لزوماً قالب پایداری برای این کار نیست.

بیشتر درباره این مفهوم

ownership

قواعدی که مشخص می‌کنند کدام مقدار مسئول نگهداری و آزادکردن یک منبع، مانند حافظه، است و این مسئولیت چگونه منتقل می‌شود.

بیشتر درباره این مفهوم

Unwinding

فرایند برگشت از مسیر اجرای تابع‌ها پس از panic که طی آن پاک‌سازی انجام می‌شود و امکان گرفتن panic با سازوکاری مانند catch_unwind وجود دارد.

بیشتر درباره این مفهوم

نویسنده مقاله «Wrong Ways To Create Strings» پس از یک ارائه کوتاه در RustLab 2026،؜ راه‌های ساخت String بدون استفاده از String::new را گردآوری کرده است. این نوشته با مسئله‌ای ساده، یعنی ساخت متن ferris،؜ سراغ روش‌هایی می‌رود که بعضی معمولی و بعضی عمداً عجیب‌اند. هدف، معرفی روشی برتر نیست؛ نویسنده این تمرین را فرصتی برای یادگیری از حل نامتعارف یک مسئله آشنا می‌داند.

نقطه شروع، جمع‌کردن character‌ها با collect یا ساخت String از byte‌های UTF-8 با String::from_utf8 است. روش بعدی، استفاده از خروجی Debug یک enum است؛ البته مقاله تأکید می‌کند که این خروجی، قالب پایداری برای Serialization نیست. مسیر دیگری هم از CString می‌گذرد: تبدیل دوباره آن به String،؜ پایان‌دهنده NUL را حذف می‌کند و معتبر بودن UTF-8 باقی‌مانده را بررسی می‌کند.

بازی با خطای CString نکته دقیق‌تری دارد. می‌توان byte‌های ردشده را از خطا پس گرفت، NUL را حذف کرد و هر byte را با char::from به character تبدیل کرد. اما این کار UTF-8 را decode نمی‌کند؛ هر مقدار u8 مستقیماً به یک Unicode code point تبدیل می‌شود. بنابراین تبدیل شکست نمی‌خورد، ولی اگر ورودی شامل character‌های چندbyte‌ای باشد، متن حاصل ممکن است اشتباه باشد.

در روش‌های دیگر، include_str! محتوای فایل را هنگام compile به‌صورت متن UTF-8 وارد برنامه می‌کند و پسوند فایل اهمیتی ندارد. همچنین می‌توان thread را ferris نامید و نامش را از درون خودش برگرداند، یا یک child process ساخت و stdout آن را به String تبدیل کرد. حتی الگوی bit یک f64 می‌تواند byte‌های متن را نگه دارد؛ استفاده از to_le_bytes وابستگی این مثال به ترتیب byte‌های دستگاه را برطرف می‌کند.

بخش حساس‌تر، ساخت String با from_raw_parts است: در این روش رعایت layout،؜ length،؜ capacity و اعتبار UTF-8 بر عهده برنامه‌نویس است. Box::into_raw،؜ ownership را بدون آزادکردن حافظه واگذار می‌کند و String مسئول آزادکردن آن می‌شود. گرفتن متن از type_name هم محدودیت دارد، چون خروجی دقیق این API تضمین نشده است.

دو ترفند پایانی، انتقال String از طریق panic و ساخت متن صرفاً با padding در format! هستند. روش panic به Unwinding نیاز دارد و با panic = "abort" کار نمی‌کند. این مجموعه بیش از آنکه نسخه‌ای برای استفاده روزمره باشد، رفتار بخش‌های مختلف Rust را با یک مسئله مشترک نشان می‌دهد.

نکته کلیدی

تبدیل char::from(u8) را با decode کردن UTF-8 یکی نگیر؛ موفق‌شدن تبدیل لزوماً به معنی درست‌بودن متن نیست. اگر ترفند catch_unwind را امتحان می‌کنی، به یاد داشته باش که با panic = "abort" متن برنمی‌گردد.

خواندن مقاله اصلی (در زبانه‌ی جدید باز می‌شود)
برچسب‌ها (۳)