iT邦幫忙

2025 iThome 鐵人賽

DAY 3
0
Rust

用刷題來練RUST系列 第 3

用刷題來練RUST Day 3 collect & 所有權 ownership & 借用檢查 borrow check

  • 分享至 

  • xImage
  •  

Day2我們可以看到將字串轉成疊代器(Iterator)後使用了collect()轉成Vec<char>,最後回傳時又將chars.into_iter().collect() }轉成String

impl Solution {
    pub fn reverse_vowels(s: String) -> String {
        let mut chars: Vec<char> = s.chars().collect();
        let (mut l, mut r) = (0, chars.len()-1);

        while l < r {
            match (is_vowel(chars[l]), is_vowel(chars[r])) {
                (false, _) => l += 1,
                (_, false) => r -= 1,
                (true, true) => {
                    chars.swap(l, r);
                    l += 1;
                    r -= 1;
                }
            }
        }

        chars.into_iter().collect()
    }
}

fn is_vowel(c: char) -> bool {
    match c {
        'a' | 'e' | 'i' | 'o' | 'u'
        | 'A' | 'E' | 'I' | 'O' | 'U' => true,
        _ => false,
    }
}

為何collect()可以回傳Vec<T>也可以是String,我們可以從下圖看到collect()後,都可以轉換成有實作FromIterator的型別,且Rust 會從上下文推斷型別,因為有回傳型別且FromIterator<char>內建可以轉成String於是自動轉成String,但若不夠清楚無法從上下文推斷型別,需要宣告型別否則系統會報錯,請你定義要轉的型別。

https://ithelp.ithome.com.tw/upload/images/20250915/20142205ZxEPTabZUC.png

來源:https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect

impl FromIterator<char> for String

https://ithelp.ithome.com.tw/upload/images/20250915/201422059BvwY4yhnf.png

來源:https://doc.rust-lang.org/std/iter/trait.FromIterator.html#impl-FromIterator%3Cchar%3E-for-String

所有權

Rust 中每個值在同一時間只能有一個擁有者

在編譯時有固定大小、存在stack上的值(i32,bool,f64)在移動(move)時是直接複製一個值給新的變數
而存在heap上的值移動(move)給另一個變數或函式時,會把所有權交出去,原本的變數就失效了,除非使用clone()複製一份另外存。

let x = 10;       // i32 是 Copy
let y = x;        // 複製一份
println!("x = {}, y = {}", x, y); // ✅ x 還能用

let s1 = String::from("hi");
let s2 = s1;      // move
// println!("{}", s1); // ❌ s1 已無效
println!("{}", s2);


借用檢查 borrow check

編譯時需要滿足

  1. 同時允許多個不可變借用 &T

    let mut x = 10;
    let r1 = &x;
    let r2 = &x;     // 多個共享引用 OK
    // let m = &mut x; // ❌ 有共享引用存活時,不能再拿可變引用
    println!("{}, {}", r1, r2);
    
    
  2. 最多只能有一個可變借用 &mut T

    let mut x = 10;
    let m = &mut x;  // 拿了獨佔引用
    *m += 1;
    // let r = &x;   // ❌ 獨佔引用存活時,不可再拿共享引用
    println!("{}", m);
    
    
  3. 1和2不能同時存在。

  4. 借用必須不懸空

    let r: &i32;
    {
        let x = 42;
        r = &x;        // ❌ x 只在內層活著
    }
    // println!("{}", r); // r 指向已被釋放的 x
    
    

所以在這裡Leetcode 345. 中宣告左右指標let (mut l, mut r) = (0, chars.len()-1)或是is_vowel(c: char)都是複製值沒有移動所有權。

最後講解回傳的chars.into_iter().collect(),來講解Vec<T>轉成迭代器的方法

  1. into_iter()拿到值所有權

  2. iter()不可變借用只能讀不能改

  3. iter_mut()可變借用能讀能改

語法 方法 拿到的元素型別 說明
for x in chars into_iter() i32 x取得所有權,chars 失去所有權
for x in &chars iter() &i32 取得不可變借用
for x in &mut chars iter_mut() &mut i32 取得可變借用
fn main() {
    let chars = vec!['a', 'b', 'c'];
    for x in chars.into_iter() {
        println!("{}",x); 
    }
    //println!("{}",chars); // ❌chars 失去所有權
}
    //a
    //b
    //c
fn main() {
    let chars = vec!['a', 'b', 'c'];
    for x in chars.iter() {
        println!("{}",x); 
    }
    println!("{:?}",chars); 
}
//印出
//a
//b
//c
//['a', 'b', 'c']
fn main() {
    let mut chars = vec!['a', 'b', 'c'];
    for x in chars.iter_mut() {
        if *x=='a' {
            *x='A';
        } 
    }
    println!("{:?}",chars); 
}
//['A', 'b', 'c']

Day3 總結

  1. Rust 中每個值在同一時間只能有一個擁有者,但存在stack上的值ex.(i32,bool,f64)在移動時是複製一個值給新的變數。
  2. 借用檢查 同時允許多個不可變借用 &T或最多只能有一個可變借用 &mut T
  3. for x in chars 會把所有權移動到x

參考資料

  1. https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect
  2. https://doc.rust-lang.org/std/iter/trait.FromIterator.html#impl-FromIterator%3Cchar%3E-for-String
  3. https://rust-lang.tw/book-tw/ch04-01-what-is-ownership.html

上一篇
用刷題來練RUST Day 2 疊代器Iterator & Match & 巨集Marco
下一篇
用刷題來練RUST Day 4 向量vector & Two Pointers
系列文
用刷題來練RUST8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言