Rust编程语言入门之函数式语言特性:-迭代器和闭包

这篇具有很好参考价值的文章主要介绍了Rust编程语言入门之函数式语言特性:-迭代器和闭包。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

函数式语言特性:-迭代器和闭包

本章内容

  • 闭包(closures)
  • 迭代器(iterators)
  • 优化改善 12 章的实例项目
  • 讨论闭包和迭代器的运行时性能

一、闭包(1)- 使用闭包创建抽象行为

什么是闭包(closure)

  • 闭包:可以捕获其所在环境的匿名函数。
  • 闭包:
    • 是匿名函数
    • 保存为变量、作为参数
    • 可在一个地方创建闭包,然后在另一个上下文中调用闭包来完成运算
    • 可从其定义的作用域捕获值

例子 - 生成自定义运动计划的程序

  • 算法的逻辑并不是重点,重点是算法中的计算过程需要几秒钟时间
  • 目标:不让用户发生不必要的等待
    • 仅在必要时调用该算法
    • 只调用一次

创建项目

~/rust
➜ cargo new closure
     Created binary (application) `closure` package

~/rust
➜ cd closure

closure on  master [?] via 🦀 1.67.1
➜ c

closure on  master [?] via 🦀 1.67.1
➜

src/main.rs 文件

use std::thread;
use std::time::Duration;

fn main() {
    let simulated_user_specified_value = 10;
    let simulated_random_number = 7;

    generate_workout(simulated_user_specified_value, simulated_random_number);
}

fn simnulated_expensive_calculation(intensity: u32) -> u32 {
    println!("calculating slowly ...");
    thread::sleep(Duration::from_secs(2));
    intensity
}

fn generate_workout(intensity: u32, random_number: u32) {
    if intensity < 25 {
        println!("Today, do {} pushups!", simnulated_expensive_calculation(intensity));
        println!("Next, do {} situps!", simnulated_expensive_calculation(intensity));
    } else {
        if random_number == 3 {
            println!("Take a break today! Remember to stay hydrated!");
        } else {
            println!("Today, run for {} minutes!", simnulated_expensive_calculation(intensity));
        }
    }
}

未用闭包优化:

use std::thread;
use std::time::Duration;

fn main() {
    let simulated_user_specified_value = 10;
    let simulated_random_number = 7;

    generate_workout(simulated_user_specified_value, simulated_random_number);
}

fn simnulated_expensive_calculation(intensity: u32) -> u32 {
    println!("calculating slowly ...");
    thread::sleep(Duration::from_secs(2));
    intensity
}

fn generate_workout(intensity: u32, random_number: u32) {
    let expensive_result = simnulated_expensive_calculation(intensity);
    if intensity < 25 {
        println!("Today, do {} pushups!", expensive_result);
        println!("Next, do {} situps!", expensive_result);
    } else {
        if random_number == 3 {
            println!("Take a break today! Remember to stay hydrated!");
        } else {
            println!("Today, run for {} minutes!", expensive_result);
        }
    }
}

优化:

use std::thread;
use std::time::Duration;

fn main() {
    let simulated_user_specified_value = 10;
    let simulated_random_number = 7;

    generate_workout(simulated_user_specified_value, simulated_random_number);
}

fn generate_workout(intensity: u32, random_number: u32) {
    
    let expensive_closure = |num| {
        println!("calculating slowly ...");
        thread::sleep(Duration::from_secs(2));
        num
    };

    if intensity < 25 {
        println!("Today, do {} pushups!", expensive_closure(intensity));
        println!("Next, do {} situps!", expensive_closure(intensity));
    } else {
        if random_number == 3 {
            println!("Take a break today! Remember to stay hydrated!");
        } else {
            println!("Today, run for {} minutes!", expensive_closure(intensity));
        }
    }
}

二、闭包(2)- 闭包类型推断和标注

闭包的类型推断

  • 闭包不要求标注参数和返回值的类型
  • 闭包通常很短小,只在狭小的上下文中工作,编译器通常能推断出类型
  • 可以手动添加类型标注
 let expensive_closure = |num: u32| -> u32 {
        println!("calculating slowly ...");
        thread::sleep(Duration::from_secs(2));
        num
    };

函数和闭包的定义语法

fn add_one_v1(x: u32) -> u32 { x + 1 }  // 函数
let add_one_v2 = |x: u32| -> u32 { x + 1 };  // 闭包
let add_one_v3 = |x| { x + 1 };  // 闭包
let add_one_v4 = |x| x + 1 ;  // 闭包

闭包的类型推断

  • 注意:闭包的定义最终只会为参数/返回值推断出唯一具体的类型
fn main() {
  let example_closure = |x| x;
  
  let s = example_closure(String::from("hello"));
  let n = example_closure(5)  // 报错
}

三、闭包(3)- 使用泛型参数和 Fn Trait 来存储闭包

继续解决 13.1 中 ”运动计划“ 程序的问题

  • 另一种解决方案:
  • 创建一个 Struct,它持有闭包及其调用结果。
    • 只会在需要结果时才执行该闭包
    • 可缓存结果
  • 这个模式通常叫做记忆化(memoization)或延迟计算(lazy evaluation)

如何让 Struct 持有闭包

  • Struct 的定义需要知道所有字段的类型
    • 需要指明闭包的类型
  • 每个闭包实例都有自己唯一的匿名类型,即使两个闭包签名完全一样。
  • 所以需要使用:泛型和 Trait Bound

Fn Trait

  • Fn traits 由标准库提供
  • 所有的闭包都至少实现了以下 Trait 之一:
    • Fn
    • FnMut
    • FnOnce
use std::thread;
use std::time::Duration;

struct Cacher<T>
where
    T: Fn(u32) -> u32,
{
    calculation: T,
    value: Option<u32>,
}

impl<T> Cacher<T>
where
    T: Fn(u32) -> u32,
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            value: None,
        }
    }

    fn value(&mut self, arg: u32) -> u32 {
        match self.value {
            Some(v) => v,
            None => {
                let v = (self.calculation)(arg);
                self.value = Some(v);
                v
            }
        }
    }
}

fn main() {
    let simulated_user_specified_value = 10;
    let simulated_random_number = 7;

    generate_workout(simulated_user_specified_value, simulated_random_number);
}

fn generate_workout(intensity: u32, random_number: u32) {
    
    let mut expensive_closure = Cacher::new(|num| {
        println!("calculating slowly ...");
        thread::sleep(Duration::from_secs(2));
        num
    });

    if intensity < 25 {
        println!("Today, do {} pushups!", expensive_closure.value(intensity));
        println!("Next, do {} situps!", expensive_closure.value(intensity));
    } else {
        if random_number == 3 {
            println!("Take a break today! Remember to stay hydrated!");
        } else {
            println!("Today, run for {} minutes!", expensive_closure.value(intensity));
        }
    }
}

使用缓存器 (Cacher)实现的限制

  • Cacher 实例假定针对不同的参数 arg,Value 方法总会得到同样的值。
    • 可以使用 HashMap 代替单个值:
      • Key:arg 参数
      • Value:执行闭包的结果
use std::thread;
use std::time::Duration;

struct Cacher<T>
where
    T: Fn(u32) -> u32,
{
    calculation: T,
    value: Option<u32>,
}

impl<T> Cacher<T>
where
    T: Fn(u32) -> u32,
{
    fn new(calculation: T) -> Cacher<T> {
        Cacher {
            calculation,
            value: None,
        }
    }

    fn value(&mut self, arg: u32) -> u32 {
        match self.value {
            Some(v) => v,
            None => {
                let v = (self.calculation)(arg);
                self.value = Some(v);
                v
            }
        }
    }
}

fn main() {
    let simulated_user_specified_value = 10;
    let simulated_random_number = 7;

    generate_workout(simulated_user_specified_value, simulated_random_number);
}

fn generate_workout(intensity: u32, random_number: u32) {
    let mut expensive_closure = Cacher::new(|num| {
        println!("calculating slowly ...");
        thread::sleep(Duration::from_secs(2));
        num
    });

    if intensity < 25 {
        println!("Today, do {} pushups!", expensive_closure.value(intensity));
        println!("Next, do {} situps!", expensive_closure.value(intensity));
    } else {
        if random_number == 3 {
            println!("Take a break today! Remember to stay hydrated!");
        } else {
            println!(
                "Today, run for {} minutes!",
                expensive_closure.value(intensity)
            );
        }
    }
}

#[cfg(test)]
mod tests {

    #[test]
    fn call_with_different_values() {
        let mut c = super::Cacher::new(|a| a);
        let v1 = c.value(1);
        let v2 = c.value(2);

        assert_eq!(v2, 2);
    }
}

  • 只能接收一个u32类型的参数和 u32 类型的返回值

四、闭包(4)- 使用闭包捕获环境

闭包可以捕获他们所在的环境

  • 闭包可以访问定义它的作用域内的变量,而普通函数则不能。
fn main() {
  let x = 4;
  
  let equal_to_x = |z| z == x;
  
  let y = 4;
  
  assert!(equal_to_x(y));
}
  • 会产生内存开销。

闭包从所在环境捕获值的方式

  • 与函数获得参数的三种方式一样:
    • 取得所有权:FnOnce
    • 可变借用:FnMut
    • 不可变借用:Fn
  • 创建闭包时,通过闭包对环境值的使用,Rust推断出具体使用哪个 Trait:
    • 所有的闭包都实现了 FnOnce
    • 没有移动捕获变量的实现了 FnMut
    • 无需可变访问捕获变量的闭包实现了 Fn

move 关键字

  • 在参数列表前使用 move 关键字,可以强制闭包取得它所使用的环境值的所有权
    • 当将闭包传递给新线程以移动数据使其归新线程所有时,此技术最为有用。
fn main() {
  let x = vec![1, 2, 3];
  let equal_to_x = move |z| z == x;
  println!("can't use x here: {:?}", x);  // 报错
  let y = vec![1, 2, 3];
  assert!(equal_to_x(y))
}

最佳实践

  • 当指定 Fn trait bound 之一时,首先用 Fn,基于闭包体里的情况,如果需要 FnOnce 或 FnMut,编译器会再告诉你。

五、迭代器(1)- Iterator trait 和 next 方法

什么是迭代器

  • 迭代器模式:读一系列项执行某些任务
  • 迭代器负责:
    • 遍历每个项
    • 确定序列(遍历)何时完成
  • Rust的迭代器:
    • 懒惰的:除非调用消费迭代器的方法,否则迭代器本身没有任何效果。
fn main() {
  let v1 = vec![1, 2, 3];
  let v1_iter = v1.iter();
  
  for val in v1_iter {
    pringln!("Got: {}", val);
  }
}

Iterator trait

  • 所有迭代器都实现了 Iterator trait
  • Iterator trait 定义于标准库,定义大致如下:
pub trait Iterator {
  type item;
  
  fn next(&mut self) -> Option<Self::Item>;
  // methods with default implementations elided
}
  • Type Item 和 Self::Item 定义了与此该 Trait 关联的类型。

    • 实现 Iterator trait 需要你定义一个 Item 类型,它用于 next 方法的返回类型(迭代器的返回类型)。
  • Iterator trait 仅要求实现一个方法:next

  • next:

    • 每次返回迭代器中的一项
    • 返回结果包裹在 Some 里
    • 迭代结束,返回 None
  • 可直接在迭代器上调用 next 方法

#[cfg(test)]
mod tests {
  #[test]
  fn iterator_demonstration() {
    let v1 = vec![1, 2, 3];
    let mut v1_iter = v1.iter();
    
    assert_eq!(v1_iter.next(), Some(&1));
    assert_eq!(v1_iter.next(), Some(&2));
    assert_eq!(v1_iter.next(), Some(&3));
  }
}

几个迭代方法

  • iter 方法:在不可变引用上创建迭代器
  • into_iter 方法:创建的迭代器会获得所有权
  • iter_mut 方法:迭代可变的引用

六、迭代器(2)- 消耗/产生迭代器

消耗迭代器的方法

  • 在标准库中,Iterator trati 有一些带默认实现的方法
  • 其中有一些方法会调用 next 方法
    • 实现 Iterator trati 时必须实现 next 方法的原因之一
  • 调用next 的方法叫做”消耗型适配器“
    • 因为调用它们会把迭代器消耗尽
  • 例如:Sum方法(就会耗尽迭代器)
    • 取得迭代器的所有权
    • 通过反复调用 next,遍历所有元素
    • 每次迭代,把当前元素添加到一个总和里,迭代结束,返回总和
#[cfg(test)]
mod tests {
  #[test]
  fn iterator_sum() {
    let v1 = vec![1, 2, 3];
    let v1_iter = v1,iter();
    let total: i32 = v1_iter.sum();
    
    assert_eq!(total, 6);
  }
}

产生其它迭代器的方法

  • 定义在 Iterator trait 上的另外一些方法叫做 ”迭代器适配器“
    • 把迭代器转换为不同种类的迭代器
  • 可以通过链式调用使用多个迭代器适配器来执行复杂的操作,这种调用可读性较高。
  • 例如:map
    • 接收一个闭包,闭包作用于每个元素
    • 产生一个新的迭代器
#[cfg(test)]
mod tests {
  #[test]
  fn iterator_sum() {
    let v1: Vec<i32> = vec![1, 2, 3];
    
    let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
    
    assert_eq!(v2, vec![2, 3, 4]);
  }
}
  • Collect 方法:消耗型适配器,把结果收集到一个集合类型中。

七、迭代器(3)- 使用闭包捕获环境

使用闭包捕获环境

  • filter 方法:
    • 接收一个闭包
    • 这个闭包在遍历迭代器的每个元素时,返回bool类型
    • 如果闭包返回 true:当前元素将会包含在 filter 产生的迭代器中
    • 如果闭包返回 false:当前元素将不会包含在 filter 产生的迭代器中
#[derive(PartialEq, Debug)]
struct Shoe {
  size: u32,
  style: String,
}

fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
  shoes.into_iter().filter(|x| x.size == shoe_size).collect()
}

#[test]
fn filter_by_size() {
  let shoes = vec![
    Shoe {
      size: 10,
      style: String::from("sneaker"),
    },
    Shoe {
      size: 13,
      style: String::from("sandal"),
    },
    Shoe {
      size: 10,
      style: String::from("boot").
    },
  ];
  
  let in_my_size = shoes_in_my_size(shoes, 10);
  
  assert_eq!(in_my_size, vec![
    Shoe {
      size: 10,
      style: String::from("sneaker")
    },
    Shoe {
      size: 10,
      style: String::from("boot")
    },
  ]);
}

八、迭代器(4)- 创建自定义迭代器

使用 Iterator trait 来创建自定义迭代器

  • 实现 next 方法
struct Counter {
    count: u32,
}

impl Counter {
    fn new() -> Counter {
        Counter { count: 0 }
    }
}

impl Iterator for Counter {
    type Item = u32;

    fn next(&mut self) -> Option<Self::Item> {
        if self.count < 5 {
            self.count += 1;
            Some(self.count)
        } else {
            None
        }
    }
}

#[test]
fn calling_next_directly() {
    let mut counter = Counter::new();

    assert_eq!(counter.next(), Some(1));
    assert_eq!(counter.next(), Some(2));
    assert_eq!(counter.next(), Some(3));
    assert_eq!(counter.next(), Some(4));
    assert_eq!(counter.next(), Some(5));
    assert_eq!(counter.next(), None);
}

#[test]
fn using_other_iterator_trait_methods() {
    let sum: u32 = Counter::new() // 1 2 3 4 5
        .zip(Counter::new().skip(1))  // 2 3 4 5 None  
        .map(|(a, b)| a * b)  // 2 6 12 20 
        .filter(|x| x % 3 == 0)  // 6 12
        .sum();  // 6 + 12 = 18

    assert_eq!(18, sum);
}

九、使用迭代器和闭包改进I/O 项目(minigrep)

src/main.rs 文件

use minigrep::Config;
use std::env;
use std::process;

fn main() {
    let config = Config::new(env::args()).unwrap_or_else(|err| {
        eprintln!("Problem parsing arguments: {}", err);
        process::exit(1);
    });
    if let Err(e) = minigrep::run(config) {
        eprintln!("Application error: {}", e);
        process::exit(1);
    }
}

src/lib.rs 文件文章来源地址https://www.toymoban.com/news/detail-405104.html

use std::env;
use std::error::Error;
use std::fs;

pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let contents = fs::read_to_string(config.filename)?;
    let results = if config.case_sensitive {
        search(&config.query, &contents)
    } else {
        search_case_insensitive(&config.query, &contents)
    };
    for line in results {
        println!("line: {}", line);
    }
    // println!("With text:\n{}", contents);
    // println!("query: {:?}", config.query);
    Ok(())
}

pub struct Config {
    pub query: String,
    pub filename: String,
    pub case_sensitive: bool,
}

impl Config {
    pub fn new(mut args: std::env::Args) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enough arguments");
        }
        args.next();

        let query = match args.next() {
            Some(arg) => arg,
            None => return Err("Didn't get a query string"),
        };
        let filename = match args.next() {
            Some(arg) => arg,
            None => return Err("Didn't get a file name"),
        };

        let case_sensitive = env::var("CASE_INSENSITIVE").is_err();
        Ok(Config {
            query,
            filename,
            case_sensitive,
        })
    }
}

pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    // let mut results = Vec::new();

    // for line in contents.lines() {
    //     if line.contains(query) {
    //         results.push(line);
    //     }
    // }

    // results

    contents
        .lines()
        .filter(|line| line.contains(query))
        .collect()
}

pub fn search_case_insensitive<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
    // let mut results = Vec::new();
    // let query = query.to_lowercase();

    // for line in contents.lines() {
    //     if line.to_lowercase().contains(&query) {
    //         results.push(line);
    //     }
    // }

    // results

    contents
        .lines()
        .filter(|line| line.to_lowercase().contains(&query.to_lowercase()))
        .collect()
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    //     fn one_result() {
    //         let query = "duct";
    //         let contents = "\
    // Rust:
    // safe, fast, productive.
    // Pick three.";

    //         assert_eq!(vec!["safe, fast, productive."], search(query, contents))
    //     }

    fn case_sensitive() {
        let query = "duct";
        let contents = "\
Rust:
safe, fast, productive.
Pick three.
Duct tape.";

        assert_eq!(vec!["safe, fast, productive."], search(query, contents))
    }

    #[test]
    fn case_insensitive() {
        let query = "rUsT";
        let contents = "\
Rust:
safe, fase, productive.
Pick three.
Trust me.";

        assert_eq!(
            vec!["Rust:", "Trust me."],
            search_case_insensitive(query, contents)
        )
    }
}

十、性能比较:- 循环 VS 迭代器

一个测试

  • 把一本小说的内容放在一个 String 里面,搜索 “the”:
test bench_search_for ... bench:  19,620,300 ns/iter (+/- 915,700)
test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
  • 迭代器的版本更快一点!

零开销抽象 Zero-Cost Abstraction

  • 使用抽象时不会引入额外的运行时开销。

音频解码器的例子

let buffer: &mut [i32];
let coefficients: [i64; 12];
let qlp_shift: i16;

for i in 12..buffer.len() {
  let prediction = coefficients.iter().zip(&buffer[i - 12..i]).map(|(&c, &s)| c * s as i64).sum::<i64>() >> qlp_shift;
  
  let delta = buffer[i];
  buffer[i] = prediction as i32 + delta;
}

到了这里,关于Rust编程语言入门之函数式语言特性:-迭代器和闭包的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Rust编程语言入门之模式匹配

    模式是Rust中的一种特殊语法,用于匹配复杂和简单类型的结构 将模式与匹配表达式和其他构造结合使用,可以更好地控制程序的控制流 模式由以下元素(的一些组合)组成: 字面值 解构的数组、enum、struct 和 tuple 变量 通配符 占位符 想要使用模式,需要将其与某个值进行

    2023年04月22日
    浏览(92)
  • Rust编程语言入门之智能指针

    指针:一个变量在内存中包含的是一个地址(指向其它数据) Rust 中最常见的指针就是”引用“ 引用: 使用 借用它指向的值 没有其余开销 最常见的指针类型 智能指针是这样一些数据结构: 行为和指针相似 有额外的元数据和功能 通过记录所有者的数量,使一份数据被多个

    2023年04月16日
    浏览(53)
  • Rust编程语言入门之无畏并发

    Concurrent:程序的不同部分之间独立的执行(并发) Parallel:程序的不同部分同时运行(并行) Rust无畏并发:允许你编写没有细微Bug的代码,并在不引入新Bug的情况下易于重构 注意:本文中的”并发“泛指 concurrent 和 parallel 在大部分OS里,代码运行在进程(process)中,OS同时

    2023年04月19日
    浏览(70)
  • Rust编程语言入门之cargo、crates.io

    通过 release profile 来自定义构建 在https://crates.io/上发布库 通过 workspaces 组织大工程 从 https://crates.io/来安装库 使用自定义命令扩展 cargo release profile: 是预定义的 可自定义:可使用不同的配置,对代码编译拥有更多的控制 每个 profile 的配置都独立于其它的 profile cargo 主要的

    2023年04月09日
    浏览(55)
  • Rust编程语言入门之最后的项目:多线程 Web 服务器

    在 socket 上监听 TCP 连接 解析少量的 HTTP 请求 创建一个合适的 HTTP 响应 使用线程池改进服务器的吞吐量 优雅的停机和清理 注意:并不是最佳实践 创建项目 main.rs 文件 修改一: 修改二: 修改三: 修改四: 修改五: hello.html 文件 404.html 文件 单线程Web服务器 开启线程 lib.r

    2023年04月25日
    浏览(58)
  • 【Rust 基础篇】Rust FFI:连接Rust与其他编程语言的桥梁

    Rust是一种以安全性和高效性著称的系统级编程语言,具有出色的性能和内存安全特性。然而,在现实世界中,我们很少有项目是完全用一种编程语言编写的。通常,我们需要在项目中使用多种编程语言,特别是在与现有代码库或底层系统交互时。为了实现跨语言的互操作性,

    2024年02月15日
    浏览(55)
  • 如何在 macOS 上安装 Rust 编程语言

    安装Rust编程语言在Mac上是一个相对简单的过程,但它可能会涉及多个步骤。在本文中,我将详细说明如何在Mac上安装Rust,并提供一些常见问题的解决方法。请注意,由于软件和工具可能会发生变化,因此建议首先查看Rust官方网站以获取最新的安装说明。 目录 1.打开终端 2

    2024年02月01日
    浏览(57)
  • 100天精通Golang(基础入门篇)——第15天:深入解析Go语言中函数的应用:从基础到进阶,助您精通函数编程!(进阶)

    🌷 博主 libin9iOak带您 Go to Golang Language.✨ 🦄 个人主页——libin9iOak的博客🎐 🐳 《面试题大全》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 🌊 《IDEA开发秘籍》学会IDEA常用操作,工作效率翻倍~💐 🪁 希望本文能够给您带来一定的帮助🌸文章粗浅,敬请批

    2024年02月12日
    浏览(68)
  • Go 与 Rust:现代编程语言的深度对比

    在快速发展的软件开发领域中,选择合适的编程语言对项目的成功至关重要。Go 和 Rust 是两种现代编程语言,它们都各自拥有一系列独特的特性和优势。本文旨在深入比较 Go 和 Rust,从不同的角度分析这两种语言,包括性能、语言特性、生态系统、适用场景以及社区支持。

    2024年04月13日
    浏览(50)
  • 【编程】Rust语言入门第4篇 字符串

    Rust 中的字符是 Unicode 类型,因此每个字符占据 4 个字节内存空间,但字符串不一样,字符串是 UTF-8 编码,也就是字符串中的字符所占的字节数是变化的(1 - 4)。 常见的字符串有两种: str,通常是引用类型, str ,即字符串字面常量,字符串切片。 std::string::String 类型 str 的变

    2024年02月20日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包