编辑:张汉东


为生命周期参数命名

假如有这样一段代码:

  1. struct Person {
  2. name: String
  3. }
  4. impl Person {
  5. pub fn name<'a>(&'a self) -> &'a str {
  6. &self.name
  7. }
  8. }

上面示例只是简单的单一生命周期参数'a,但我们将其重写命名为'me,可能会更加直观:

  1. struct Person {
  2. name: String
  3. }
  4. impl Person {
  5. pub fn name<'me>(&'me self) -> &'me str {
  6. &self.name
  7. }
  8. }

在下面一些场景中,命名生命周期参数可能更好:

场景一

  1. use once_cell::unsync::OnceCell;
  2. struct Provider {
  3. data_cache: OnceCell<Data>,
  4. metadata_cache: OnceCell<Metadata>,
  5. }
  6. // ...
  7. fn process_data(data: &Data) -> Result<&str> {
  8. // ...
  9. }

修正为:

  1. fn process_data<'prov>(data: &'prov Data) -> Result<&'prov str> {
  2. // ...
  3. }

将生命周期参数命名为 'prov 有助于标识 data 数据来自于 Provider结构体实例。 在 Rust 编译器源码中也能见到'tcx这样的命名,用来标识这是类型上下文(typing context )。

场景二

  1. struct Article {
  2. title: String,
  3. author: Author,
  4. }
  5. #[derive(PartialEq, Eq)]
  6. struct Author {
  7. name: String,
  8. }
  9. struct ArticleProvider {
  10. articles: Vec<Article>,
  11. }
  12. struct AuthorProvider {
  13. authors: Vec<Author>,
  14. }
  15. // 这里具有两种生命周期参数命名
  16. struct AuthorView<'art, 'auth> {
  17. author: &'auth Author,
  18. articles: Vec<&'art Article>,
  19. }
  20. // 这里具有两种生命周期参数命名
  21. // 在需要指定两个生命周期参数长短关系的时候可以通过 'auth : 'art 这种方式指定,但是此处不需要
  22. fn authors_with_articles<'art, 'auth>(
  23. article_provider: &'art ArticleProvider,
  24. author_provider: &'auth AuthorProvider,
  25. ) -> Vec<AuthorView<'art, 'auth>> {
  26. author_provider
  27. .authors
  28. .iter()
  29. .map(|author| {
  30. let articles = article_provider
  31. .articles
  32. .iter()
  33. .filter(|article| &article.author == author)
  34. .collect();
  35. AuthorView { author, articles }
  36. })
  37. .collect()
  38. }

小结

将生命周期参数重新命名,面对使用引用比较复杂的场景,可以增加可读性,方便开发者分析生命周期参数。这算得上一个最佳实践。

来源: https://www.possiblerust.com/pattern/naming-your-lifetimes

优化技巧:Rust 中 match 分支语句中避免使用 ?

来自微信群:David Li

最近碰到rust的一个坑,在match 的分支里面使用?可能会导致栈内存特别大。

有一个函数,match 一个 Enum,这个Enum 有十几个定义,因此match 有十几个分支,每个分支里面最后都会调一个函数,返回值是Result,因此使用了?

上周测试报告说 debug 版本跑查询进程会崩掉,分析发现栈溢出了。我们上层代码把线程栈设置为 512 KB,然后调试发现某些函数栈内存竟然用了几百KB。

代码示意:

  1. match SomeEnum {
  2. One(_) => get_result()?,
  3. Two(_) => get_result()?,
  4. Three(_) => get_result()?,
  5. Four(_) => get_result()?,
  6. Five(_) => get_result()?,
  7. //...
  8. }

最后把match分支的Result去掉?,把 match表达式赋值 给临时变量之后再用?,内存占用降下来了。

代码示意:

  1. let get_res = match SomeEnum {
  2. One(_) => get_result(),
  3. Two(_) => get_result(),
  4. Three(_) => get_result(),
  5. Four(_) => get_result(),
  6. Five(_) => get_result(),
  7. //...
  8. };
  9. let res = get_res?

P.S : 还可以获得一个优化技巧是:先把栈内存调低点,让这种问题尽早暴露。