动态

详情 返回 返回

rust進階-基礎.2.Option類型 - 动态 详情

Option類型是Rust中非常重要的一個類型,和Result也類似。

本文主要根據文檔:枚舉類型Option編寫

主要闡述以下內容:

1.Option和Result比較

2.Option的主要方法

3.示例

 

1.Option和Result比較

以下內容來自於文心一言

特性 Option Result
目的 表示一個值可能存在(Some(T))或不存在(None),避免空指針異常。 表示一個操作可能成功(Ok(T))或失敗(Err(E)),需攜帶錯誤信息。
典型場景 集合查找(如Vec::get)、可選字段(如鏈表節點的next指針)。 文件I/O、網絡請求、解析驗證(如JSON反序列化)。
錯誤處理 僅表示值的存在性,無法傳遞錯誤上下文(如None無法區分“未找到”與“空輸入”)。 通過Err(E)攜帶錯誤類型,支持精細化錯誤處理(如區分“文件不存在”與“權限不足”)。

這些應該是主要的差異。

Result可以傳遞錯誤信息。

 

 

2.Option的主要方法

 

 

 

 

3.示例

use std::iter::Sum;
#[derive(Debug)]
struct Area {
    width: u32,
    height: u32,
    flag: bool,
}

impl Default for Area {
    fn default() -> Self {
        Area {
            width: 103,
            height: 2012,
            flag: true,
        }
    }
}
impl Iterator for Area {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.flag {
            let area = self.width * self.height;
            self.flag = false;
            Some(area)
        } else {
            None
        }
    }
}
/**
 * 實現這個以便直接比較兩個Area對象的大小,因為Rust不允許直接對結構體進行比較
 */
impl PartialEq for Area {
    fn eq(&self, other: &Self) -> bool {
        let self_area = self.width * self.height;
        let other_area = other.width * other.height;
        self_area == other_area
    }
}
/**
 * 實現這個以便直接比較兩個Area對象的大小,因為Rust不允許直接對結構體進行比較
 */
impl PartialOrd for Area {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        let self_area = self.width * self.height;
        let other_area = other.width * other.height;
        Some(self_area.cmp(&other_area))
    }
}
impl Area {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

#[derive(Debug)]
struct Exam{
    test_time:u32,
    score:u32,
}
impl Sum for Exam{
   fn sum<I>(iter: I) -> Self
       where I: Iterator<Item = Exam>{
        let mut total=Exam{test_time:0,score:0};
        for x in iter{
            if total.test_time==0 {
                total.test_time = x.test_time;
            }            
            total.score += x.score;
        }
        total
  }
}


fn main() {
    println!("\n-- 測試直接操作   \n");
    let _r = test_direct_add(Some(1), Some(2));
   

    //兩個同種Option直接比較
    println!("\n-- 測試直接比較   \n");
    test_direct_compare();
    
    //
    println!("\n-- 測試抽取值相關的方法   \n");
    test_extract_value();

    println!("\n-- 測試修改相關的方法   \n");
    test_modify();

    println!("\n-- 測試操作相關的方法   \n");
    test_operate();

    println!("\n-- 測試查詢   \n");
    let value = Some(String::from("健康飲食。66天養成一個習慣"));
    test_query(value);

    println!("\n-- 測試引用相關的方法   \n");
    test_ref();

    println!("\n-- 測試轉換相關的方法   \n");
    test_transform();
   
}

/**
 * 兩個Option可以直接比較,如果二者都實現了PartialOrd(Ord也可以)
 * 那麼可以使用基本的比較運算符進行比較
 */
fn  test_direct_compare(){
    println!("要求其中的T實現PartialOrd");
    let d=Area::default();
    let a=Area{width: 10, height:20, flag:true};
    if d>a{
        println!("{:?}>{:?}",d,a);
    }else{
        println!("{:?}<={:?}",d,a);
    }
}

/**
 * 直接使用?操作符,如果Option是Some則返回裏面的值,如果是None則返回None。
 */
fn test_direct_add(a: Option<i32>, b: Option<i32>) -> Option<i32> {
    let result=Some(a? + b?);
    println!("{}+{}={}",a.unwrap(),b.unwrap(), result.unwrap());
    result
}

/**
 * 取值後,通常而言,原來的變量不可在引用
 */
fn test_extract_value() {
    //測試unwrap_or 和unwrap_or_else
    //unwrap_or_else 要求參數是一個FnOnce類型的匿名函數,且返回T類型值。

    let book = None;
    let book_name = book.unwrap_or("沈氏四聲考".to_string()); //unwrap_or獲取默認值
    println!("None unwrap_or後,值={}", book_name);
    let book = Some("沈氏四聲考".to_string());
    let book1=Some("沈氏四聲考".to_string());
    let book_name = book.unwrap_or_else(|| "沈氏四聲考?".to_string()); //unwrap_or獲取默認值
    println!("{:?}中的值通過unwrap_or_else={}", book1,book_name);

    //測試unwrap_or_default,此方法要求T類型實現Default trait,所以如果沒有值情況下,返回的是該類型的默認值。
    let area: Option<Area> = None;
    let area_default = area.unwrap_or_default(); //獲取默認值
    println!("Area是一個None,默認={:?}", area_default.area());

    //測試take,take_if,replace等方法。
    // take --take(&mut self) -> Option<T>  ,原來的變為None
    // take_if take_if<P>(&mut self, predicate: P) -> Option<T>。p:FnOnce(&mut T) -> bool
    //         在取的時候,可以對數據進行修改
    // replace replace(&mut self, value: T) -> Option<T>,替換原來的值,返回來源的值。關鍵是原來的保留,新的也存在.

    let mut a=Some(10);
    let ta=a.take(); //取出值,並把原來的值設置為None。
    println!("{:?}被取出後={:?}",ta, a);
    //take_if 要求參數是一個FnOnce類型的匿名函數,且返回bool類型值。
    let mut b=Some(20);
    let tb=b.take_if(|x| *x>30);
    println!("{:?}被取出後={:?}",tb, b);
    //replace 要求參數是T類型值。
    let mut c=Some(30);
    let old=c.replace(40);
    println!("c={:?},old={:?}", c,old);

}

/**
 * 測試如何修改一個Option內含值
 */
fn test_modify(){
   //方式有多種,已知的略。
   println!("演示insert,get_or_insert方法。");
   //此處介紹insert,get_or_insert方法。
   //insert(&mut self, value: T) -> &mut T,會替換原來的值(如果有)
   //get_or_insert(&mut self, value: T) -> &mut T , 如果沒有值,則插入,否則取出已有的值。
   //不太明白這些方法有什麼使用場景?

   let mut a=Some(10);
   let b=a.insert(20);
   *b+=10;
   println!("b={:?}", b);
   println!("a={:?}", a.unwrap());

   let mut a1=None;
   let c=a1.get_or_insert(30);
   println!("c={:?}", c);


}

fn test_operate(){
    //測試and - and只要有一個是None,則返回None,否則返回參數值
    //不知道這方法有何用? 就是用於測試兩個Option?
    //除了and,還有or和xor。
    //or取其中一個值,如果有一個值是Some,則取之。如果變量本身是Some,則返回本身
    //xor和or基本一樣,不同的是如果x.xor(y)中x,y都是Some,則返回None。

    let a = Some(10);
    let b:Option<u32> = None;
    let c=a.and(b);
    println!("c={:?}", c);

    //測試and_with
    let fx=|_|{Some(100)};
    let c=a.and_then(fx);
    println!("c={:?}", c);

}

fn test_query(value: Option<String>) {
    if value.is_some() {
        println!("{}", value.unwrap());
    } else {
        println!("沒有值");
    }
}

/**
 * rust可以象js那麼隨意,在函數中定義函數
 */
fn test_ref() {
    fn validate_type(val: Option<&String>) {
        match val {
            Some(v) => println!("{}", *v),
            None => println!("沒有值"),
        }
    }
    let a = Some(String::from("今君往死地,沉痛迫中腸"));
    let ref_a = &a; // &Option<String>
    let ref_a_ref = ref_a.as_ref(); //Option<&String>
    validate_type(ref_a_ref);
}


fn test_transform() {
    let book: Option<i32> = None;
    let book_name = book.ok_or(-1);
    println!("{:?}", book_name);

    //測試無聊函數filter等。
    //filter函數要求T本身實現Iterator trait. struct轉為迭代器本身就是很罕見的。
    let score = Some(10);
    let dd = score.filter(|x| *x >= 10); // filter要求FnOnce的參數是&T類型。
    println!("過濾後,{:?}", dd.unwrap());

    //測試map方法。
    //map返回Option<U>
    let area_default= Some(Area::default());
    println!("默認值={:?}", area_default);
    let area_new = area_default.map(|a| {
       a.area()
    });
    println!("area_new={:?}", area_new);

    //測試合併 x.zip(y)=Some((x,y)),如果其中一個為None則返回None。
    let rubbish=score.zip(area_new);
    println!("合併結果1={:?}", rubbish);
    let rubbish=book.zip(score);
    println!("合併結果2={:?}", rubbish);
    let rubbish=score.zip(book);
    println!("合併結果3={:?}", rubbish);

    //迭代行為,這是一種自動行為,不需要顯式調用。
    let score:[u32;3]=[10,20,30];
    let score_vec:Vec<u32>=score.into_iter().chain(Some(90)).collect();
    println!("合併結果4(利用迭代器)={:?}", score_vec);
    //如果T實現了Sum,Product等trait,則可以調用sum,product等方法。 但是如果有一個成員為None,則返回None。
    let score1=Some(Exam{
        score:10,
        test_time:1994
    });
    let score2=Some(Exam{
        score:20,
        test_time:1994
    });
    let score_arr = [score1,score2];
    let sum:Option<Exam>=score_arr.into_iter().sum();
    println!("總成績={:?}", sum);
    
    //收集
    //收集Option還是有一些價值的.
    //注意:只要其中過一個是None,則返回None。

    let v:[Option<u32>;4] = [Some(2), Some(4), None, Some(8)];
    let res: Option<Vec<u32>> = v.into_iter().collect();
    println!("通過collect收集的結果5(收集)={:?}", res.unwrap_or_default());
    //展示一個不是None的集合,返回的還是Option<Vec<T>>,所以需要unwrap一下。
    let v:[Option<u32>;4] = [Some(2), Some(4), Some(880), Some(8)];
    let res: Option<Vec<u32>> = v.into_iter().collect();
    println!("通過collect收集的結果5(收集)={:?}", res.unwrap());

}

 

輸出示例:

 

Add a new 评论

Some HTML is okay.