字符串与切片

Rust的字符串类型是String。 str是一个不可变的字符串切片,通常以引用的形式出现,&str

切片

// 字符串
fn main() {
    // 切片
    let s = String::from("hello world");

    // let s = "hello world"; // 效果一致

    let s1 = &s[..2]; // 0到 索引2前面一位
    let s2 = &s[3..]; // 3到 末尾
    let s3 = &s[6..9]; // 6 到 8
    let s4 = &s[..]; // 完整的

    println!("s: {}", s);
    println!("s1: {}", s1);
    println!("s2: {}", s2);
    println!("s3: {}", s3);
    println!("s4: {}", s4);
    println!("s: {}", s); // 不会影响原 s
}

中文字符串切片问题

fn main() {
    println!("---chinese_error---");
    let s = "这么难吗";
    // let s1 = &s[..2]; // 会报错

    // 每个汉字占用三个字节
    // 若切的不是三的倍数 会使得下刀处不在边界上 切不出完整的汉字 则报错
    /*
    因为在 Rust 中,
    字符串是 UTF-8 编码的,
    而 s[..2] 试图对字符串切片时以字节为单位,
    而不是以字符为单位。
    这会导致潜在的无效切片,
    因为 UTF-8 编码的字符可能占用多个字节。
    例如,中文字符通常占用 3 个字节。
    */

    let s1 = &s[..3]; // 切出 【这】 字
    println!("s1: {}", s1);
}

切片问题

fn main() {
    println!("---easy_error---");
    fn slice_str(s: &String, stat: usize, end: usize) -> &str {
        &s[stat..end]
    }

    let mut s = String::from("hello");

    let world = slice_str(&s, 1, 3); // 这里有不可变引用
                                     // s.clear(); // 这里存在可变引用
                                     // 违背了可变与不可变同时存在原则
    println!("world: {}", world);
    // world 生命周期结束 不可变引用被释放
    s.clear(); // 不会报错
}

字符串操作

fn main() {
    println!("---update_string---");

    let mut s = String::from("hello");

    println!("s: {}", s);
    // 追加
    s.push_str(", world!");
    println!("s追加字符串: {}", s);

    // 插入
    s.insert_str(0, "before ");
    println!("s插入: {}", s);

    // 替换1 [replace](适用于String 于 &str) ( 将返回一个新的String 而不是修改原值
    let s1 = s.replace("hello", "hi");
    println!("s替换hello为hi后s1: {}", s1);

    let s2 = "hello rust, I love rust, rust_over";
    let s3 = s2.replace("rust", "R_U_S_T"); // 所有匹配的字符都将替换

    println!("s3:{} => {}", s2, s3);

    // 替换2 [replacen] (适用于String 于 &str) ( 需要指定次数 将返回一个新的String 而不是修改原值
    let s4 = s2.replacen("rust", "PYTHON", 2); // 前两个匹配的被替换,后面的不替换
    println!("s4:{} => {}", s2, s4);

    // 替换3 [replace_range](只适用String)(会直接修改原值不会返回新类型 但要注意字符边界问题
    let mut s5 = String::from("rust好难");
    s5.replace_range(7.., "简单");
    println!("s5:{}", s5);

    // 删除
    /*
    以下方法都是直接修改原值,所以只适用于String
    pop 删除并返回最后一个字符
    remove 删除并返回指定位置的字符 (注意字符边界问题
    truncate 删除指定位置后的字符串 没有返回值 (注意字符边界问题
    clear 清空 没有返回值
    */
    let mut s6 = String::from("在很久很久以前");
    let s6_pop = s6.pop();
    println!("s6 pop:{}, {:?}", s6, s6_pop);

    let s6_remove = s6.remove(0);
    println!("s6 remove:{}, {:?}", s6, s6_remove);

    s6.truncate(6);
    println!("s6 truncate:{}", s6);

    s6.clear();
    println!("s6 clear:{}", s6);

    // 连接
    // 使用 + 运算符  加号的左边只能是String类型 且运算后会移交所有权至等号左边的新变量 加号的右边必须为 字符串切片类型
    let s7 = String::from("一二三");
    let s8 = String::from("四五六");
    let s9 = "七八九";

    let s10 = s7 + &s8; // s7的所有权move到s10上了
                        // println!("s7: {}", s7); // 这里将无法再访问s7
    println!("s8: {}", s8); // 不会影响s8 因为在上面的 +运算 中 s8是不可变引用 不移交所有权
    println!("s10: {}", s10);
    let s11 = s10 + s9;
    println!("s11: {}", s11);

    let s12 = String::from(" ");
    let s13 = String::from("world");
    // 亦或 format
    let s14 = format!(
        "{}{}{}{}",
        s12,
        "hello",
        &s13,
        "!"
    );
    println!("s14: {}", s14);
    println!("s13: {}", s13);
    println!("s12: {}", s12);
    // 为什么这里可以继续使用s12 不应该是s12的所有权被移交给format函数了吗
    // 答:format! 是一个宏 与普通函数不同,不会将变量的所有权移动到参数上
}

字符与字节

#![allow(unused)]
fn main() {
fn string_char() {
    println!("---update_string---");
    let s1 = "rust好难";
    for char in s1.chars() {
        println!("char: {}", char);
    }

    for byte in s1.bytes() {
        println!("byte: {}", byte);
    }
}

}