<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Amirreza Ask</title>
        <link>https://virgool.io/feed/@amirrezaask</link>
        <description>مهندس نرم افزار در اسنپ</description>
        <language>fa</language>
        <pubDate>2026-06-07 15:09:08</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/21480/avatar/S0aBuk.png?height=120&amp;width=120</url>
            <title>Amirreza Ask</title>
            <link>https://virgool.io/@amirrezaask</link>
        </image>

                    <item>
                <title>چطوری یه وب سرور بسازیم ؟</title>
                <link>https://virgool.io/@amirrezaask/%D9%88%D8%A8-%D8%B3%D8%B1%D9%88%D8%B1-%DA%86%DB%8C-%DA%A9%D8%A7%D8%B1-%D9%85%DB%8C%DA%A9%D9%86%D9%87-ov6cb3qdqhxu</link>
                <description>.ریچارد فاینمن میگه &quot;When I cannot create it, I do not understand it&quot; ... اگر نتونم چیزی رو بسازم پس نمیتونم بفهممش مدتها به عنوان برنامه نویس وب کلمه http رو شنیدم و باهاش کار کردم اما به طور دقیق نمیدونستم چی کار میکنه چون همیشه از یه فریمورک یا لایبرری استفاده میکردم که این لایه رو برای من می پوشوند . چند وقت پیش شروع به خوندن کتاب Rust Programming Language کردم . پروژه آخر این کتاب نوشتن یک وب سرور اول به صورت Single Thread و بعد به صورت Multi Thread بود که باعث شد ایده ی این پست به ذهنم بخوره .یه وب سرور یه طور خلاصه کاری که میکنه اینه که روی یه پورت خاص که ما بهش میگیم ( عموما 80 یا 81 ) گوش میده و منتظر یه کانکشن میمونه بعد از اینکه 3-step handshake انجام شد حالا کلاینت ما یه درخواست http میفرسته و منتظر جواب میمونه و از اینجاست که کار ما شروع میشه . خب برای درک بهتر شروع کنیم به نوشتن کد .اول یه پروژه جدید با Rust ایجاد میکنیم ...cargo new webserver --binاینجا با کمک cargo ( که در واقع ابزار مدیریت بسته و پروژه Rust هست ) یه پروژه به نام webserver درست کردیم .این پروژه ای هست که ساخته شده ... نسبتا هیچی نداره نه ؟؟خب اولین کاری که باید بکنیم اینه که روی port ی که بهمون میگن منتظر یه کانکشن tcp بمونیم . فعلا این port رو ثابت بگیریم تا بعدا به عنوان ورودی از کاربر بگیریمش .برای اینکه روی یه port گوش بدیم از لایبرری استاندارد Rust و ماژول TcpListener استفاده میکنیم .let listener = TcpListener::bind(&quot;127.0.0.1:8080&quot;).unwrap();خب با این خط کد ما یه TcpListener درست کردیم که روی لوکال هاست و پورت 8080 گوش میده و منتظر یه کانکشن Tcp میمونه .خب حالا فرض کنیم یه کانکشن اومد چی کارش کنیم ؟for stream in listener.incoming() {
    let mut stream = stream.unwrap();
    خب الان با اومدن هر ریکویست ما اون رو دریافت میکنیم و میتونیم باهاش هرکاری بخوایم بکنیم !اول بیاین بفهمیم یه ریکویست http به صورت خام چه شکلیه ؟  let mut buffer = [0; 512];      
  stream.read(&amp;mut buffer).unwrap(); 
  let buffer = String::from_utf8_lossy(&amp;buffer[..])
  println!(&quot;{}&quot;, buffer)خب در اینجا ما یه آرایه 512 تایی از اعداد درست میکنیم که در واقع ریکویست ما قبل از تبدیل شدن به رشته توی اون قرار بگیره و بعد ریکویست رو میخونیم و توی این آرایه قرار میدیم و در مرحله بعد اون رو تبدیل به یک String میکنیم .خب حالا وقتی Println انجام میشه یه همچین خروجی خواهیم داشت ...GET / HTTP/1.1Host: localhost:7878Connection: keep-aliveCache-Control: max-age=0Upgrade-Insecure-Requests: 1User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.103 Safari/537.36Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3Accept-Encoding: gzip, deflate, brAccept-Language: en-GB,en;q=0.9,en-US;q=0.8,fa;q=0.7,la;q=0.6,ar;q=0.5Cookie: csrftoken=SLaqNbhHwKU** یه نکته در مورد سینتکس اگر نا آشناس اینه که Rust از یک سیستم جدید در کامپایلرش استفاده میکنه و برای اینکه بتونه کد های مدیریت حافظه رو تولید کنه نیاز داره با بهش کمک کنیم به همین خاطر بعضی وقتا کدای Rust یه خرده عجیب به نظر میان **خب حالا بیایم به ازای هر ریکویست یه جواب ثابت رو بفرستیم .let response = &quot;HTTP/1.1 200 OK\r\n\r\nHelloAgain&quot;;
stream.write(response.as_bytes()).unwrap();
stream.flush().unwrap();با اجرای کد و رفتن به صفحه localhost:7878 این صفحه رو میبینید HelloAgain!!!!خب تونستیم که اولین صفحه وب رو بدون استفاده از هیچ لایبرری و صرفا Tcp نشون بدیم مرحله بعد چیه ؟اینکه به جای متن ساده یه صفحه html رو نشون بدیم .برای اینکار باید اول یک فایل html رو بخونیم  let contents = fs::read_to_string(&quot;hello.html&quot;).unwrap(); 
   let response = format!(&quot;HTTP/1.1 200 OK\r\n\r\n{}&quot;, contents);برای اینکار کافیه کد های بالا رو تغییرات بدیم تا جواب ریکویست شامل متن کامل یک فایل مثلا hello.html باشه . حالا فرض کنیم شما html رو مثلا به این شکل مینویسید  &lt;html lang=&quot;en&quot;&gt;
  &lt;head&gt;
   &lt;meta charset=&quot;utf-8&quot;&gt; 
   &lt;title&gt;Hello!&lt;/title&gt; 
   &lt;/head&gt; 
   &lt;body&gt; 
   &lt;h1&gt;Hello&lt;/h1&gt; 
   &lt;p&gt;Salam man be to yare ghadimi&lt;/p&gt; 
   &lt;/body&gt; 
   &lt;/html&gt; این فایل رو در روت پروژه یعنی جایی که فایل های .toml وجود دارن قرار میدیم .خب بازم اگر برنامه مون رو اجرا کنیم اینبار صفحه html بالا رو بهمون نشون میده عالی شد نه ؟البته این برنامه فقط برای این خوبه که بفهمیم http اونقدرا هم پیچیده نیست و چطوری داره کار میکنه وگرنه برنامه ما تا یه وب سرور مثل IIS یا Apache یا Nginx خیلی خیلی فاصله داره .مثل همیشه نظراتتون خوش حالم میکنه .</description>
                <category>Amirreza Ask</category>
                <author>Amirreza Ask</author>
                <pubDate>Fri, 19 Apr 2019 22:14:17 +0430</pubDate>
            </item>
                    <item>
                <title>چرا به Go2 نیازی نداریم ؟</title>
                <link>https://virgool.io/golangpub/%DA%86%D8%B1%D8%A7-%D8%A8%D9%87-go2-%D9%86%DB%8C%D8%A7%D8%B2%DB%8C-%D9%86%D8%AF%D8%A7%D8%B1%DB%8C%D9%85-rwbknlz6htzy</link>
                <description>این چند ماه اخیر بحث های خیلی زیادی هم در توییتر هم در ردیت درباره Go2 داره میشه و تا الان 3 پروپوزال برای 3 تا فیچر جدید داده شده ( البته از طرف کامیونیتی ) خب ببینیم این 3 پروپوزال چی هستن ؟اررور هندلینگ اررور ها در گو بر خلاف اکثر زبون های امروزی Exception نیستن و همیشه جزیی از خروجی فانکشن هستن که عموما به این شکل هندل میشن func someFunc(path string) (string, error){
    file1, err := func1(path)
    if err != nil {
            return &quot;&quot;, fmt.Errorf(&quot;Some error while func1 : %v&quot;, err)
    }
     file2, err := func2(path)    
      if err != nil { 
             return &quot;&quot;, fmt.Errorf(&quot;Some error while func2 : %v&quot;, err)   
       } 
    file3, err := func3(path)           
    if err != nil {               
             return &quot;&quot;, fmt.Errorf(&quot;Some error while func3 : %v&quot;, err)
     }  
       file4, err := func4(path)           
       if err != nil {               
           return &quot;&quot;, fmt.Errorf(&quot;Some error while func4 : %v&quot;, err)
     }  
       file5, err := func5(path)          
        if err != nil {              
         return &quot;&quot;, fmt.Errorf(&quot;Some error while func5 : %v&quot;, err) 
      }  
    return file, nilنقطه ضعف این روش اینه که اگر فرض کنیم در یک فانکشن شما 5 فانکشن کال داشته باشید و برای همه بخواید خروجی رو چک کنید باید نزدیک 20 خط کد بنویسید که یه مقدار نسبت به کاری که میکنید زیاده حالا ببینیم پروپوزال اول چی ارایه میکنهfunc someFunc(path string) (string, error) {
    handle err {
        return fmt.Errorf(&quot;Some error while reading file : %v&quot;, err)
    }
    file1 := check func1(path)
    file2 = check func2(path)
    file3 := check func3(path)
    file4 := check func4(path)
    file5 := chech func5(path)
    return file5
  }خب نسبتا خلاصه شد نه ؟؟توی این سناریو کد ما خیلی خلاصه تر شد و البته به قیمت اضافه شدن دو کیوورد به زبون و یک لایه پیچیدگی.حالا این سناریو رو در نظر بگیرید func someFunc(path string) (string, error) {
    file1,err := func1(path)
    if err != nil {
        if err == errType1 {
            return &quot;&quot;, fmt.Errorf(&quot;some error :%v&quot;, err)
        }
        if err == errType2 {
            //some kind of retry mechanism
            return someFunc(path)
        }
        if err == errType3 {
            panic(err)
        }
    }
}خب توی این سناریو دیزاین ارایه شده نه تنها کمکی نمیکنه بلکه عملا غیر قابل استفاده است ... پس چرا فیچری به زبون اضافه شه که در خیلی موارد عملا استفاده نمیشه ؟بهبود اررور ها ( ساختار درختی, فرمت بهتر موقع نمایش )این پروپوزال به طور کلی در مورد wrap کردن اررور ها توی تایپ های مخصوص به خودشون صحبت میکنه مثلا type DatabaseError struct {
    err error
    DatabaseName string
}
func (e *DatabaseError) Unwrap() error {
    return err
}
func (e *DatabaseError) Formatter () string{
    return someFormatter(e.err)
}در ابتدا پیشنهاد میده که دو اینترفیس به استاندارد لایبرری اضافه شه که تغییر خیلی مهمی نیست اما اضافه است چون دقیقا کاری که این پروپوزال پیشنهاد میده رو pkg/errors انجام میده و عملا نیازی به تغییر نیست بخش بعدی هم در واقع کاملا وابسته به جنریک هاست که تو بخش بعدی بررسی میکنیم .جنریک هاجنریک ها شاید پر بحث ترین و مهمترین بخش این 3 پروپوزال باشن . فیچری که از همون روزهای اول گو سرش بحث بود و در خوب بودنشون شکی نیست اما دو تا بحث مهم در مورد اونها وجود داره اولی آسیب زدن به سادگی کد و دومی پیاده سازی .بخش دوم توی پروپوزال تا حدی بحث شده و البته من دانش نظر دادن در مورد خوب یا بد بودن این پیاده سازی رو ندارم بهتره خود گو تیم نظر بدن اما در مورد آسیب زدن به سادگی به نظرم نکته مهمیه .عموما وقتی بخوایم به این مورد فکر کنیم بهتره یه همچین چیزی رو اول ببینیم public class SomeTypeOfCat&lt;? extends Cat&lt;? extends Mamal&lt;? extends Animal&lt;? extends SomeThingAlive&lt;? extends SomeThing&gt;&gt;&gt;&gt;&gt;ممکنه بگید که این کد یه کد بده یا اینکه اصلا در گو ارث بری وجود نداره اما این یک مثاله از اینکه جنریک ها به خاطر ماهیتشون چطور میتونن باعث پیچیده شدن کد بشن تازه از سربار موقع کامپایل همچین چیزی بگذریم . جنریک ها در بین این 3 پروپوزال ویژگی نسبتا معقولی هستن ولی هنوز همون دو سوال اول به درستی پاسخ داده نشدن بحث سادگی و پیاده سازی . جالبه که بدونید گو در استاندارد لایبرری خودش شکلی از جنریک ها رو داره برای تایپ هایی مثل اسلایس ها یا مپ ها ولی چون تمام این تایپ ها مربوط به استاندارد لایبرری هستن و به طور محدود پیاده سازی شدن مشکلی پیش نمیارن در این مورد این مقاله توضیح خیلی خوبی از نحوه پیاده شدن این تایپ ها در استاندارد لایبرری ارایه میده .خب اما این پروپوزال چه چیزی ارایه میده func echo(type T) (arg T) T {
    return arg
}
type LinkedList(type T) []Tخب بازم به نظر میاد خیلی خوب باشه نه ؟ میشه فانکشنی نوشت یا تایپی تعریف کرد که به شکل تایپ سیف رو هر تایپی کار کنه ولی خب طبق معمول این بخش شیرین ماجراست وقتی جنریک ها بحثشون پیش میاد بحث بعدی اینه که به چه شکل میشه یه گروه خاص از تایپ ها رو مجاز دونست که تو مثال جاوایی بالا تایپ هایی که از یک کلاس خاص ارث بری داشتن مجاز بودن اما این موضوع توی این پروپوزال چطور حل شده ؟contract Equal (t) {
    t == t
}
contract Graph(n Node, e Edge) {
    var edges []Edge = n.Edges()
    var nodes []Node = n.Nodes() 
}
توی این پروپوزال برای محدود کردن تایپ های مورد پذیرش مفهوم جدیدی به اسم contract تعریف شده ( که من نفهمیدم چرا از اینترفیس ها استفاده نشده البته اینترفیس ها باید تغییراتی بکنن تا بتونن همه کارای contract ها رو انجام بدن ولی خب باز بهتر از یه چیز جدیده ) و این contract ها یک یا چند رفتار رو برای تایپ تعریف می کنن که اگر اون تایپ بتونه این کار ها رو انجام بده پذیرفته میشه مثلا در contract اول اگر اپراتور == بتونه رو این تایپ کار کنه پس این تایپ پذیرفته میشه یا توی contract دوم اگر متدی به اسم Edges باشه روی تایپ دوم و یک آرایه از Edge ها برگردونه تایپ پذیرفته میشه . contract دوم دقیقا همون کاری رو انجام میده که اینترفیس ها انجام میدن و عملا پیچیدگی هست که اضافه است اما contract اول کاری رو انجام میده که اینترفیس ها قادر به انجامش نبودن و البته پیشنهاد من اینه که اگر قراره این رفتار اضافه بشه به شکلی به اینترفیس ها اضافه بشه .در کل پروپوزال 3 از بقیه قوی تره دلیلش هم اینه که زمان طولانی تری روش بحث شده اما بازم با فرهنگ زبون گو به نظر من سازگار نیست .خب امیدوارم مفید بوده باشه نظراتتون چه اینجا چه توییتر خوشحالم میکنه . </description>
                <category>Amirreza Ask</category>
                <author>Amirreza Ask</author>
                <pubDate>Mon, 08 Apr 2019 09:30:27 +0430</pubDate>
            </item>
            </channel>
</rss>