From b269fcf2056fb07326186fc8824478a965f3e2bf Mon Sep 17 00:00:00 2001 From: xfy Date: Wed, 15 Jul 2026 11:46:56 +0800 Subject: [PATCH] =?UTF-8?q?perf(posts):=20list/search=20=E9=9B=B6=20capaci?= =?UTF-8?q?ty=20Vec=20=E6=94=B9=20collect=20=E9=A2=84=E5=88=86=E9=85=8D=20?= =?UTF-8?q?+=20helpers=20retain?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit list.rs 5 处、search.rs 1 处用 Vec::new()+循环 push,初始 capacity 为 0, 循环触发多次指数 realloc。rows.iter().map().collect() 用 slice 迭代器的 精确 size_hint 一次性预分配。首页/列表/搜索/标签分页路径。 helpers.rs row_to_post_list_item / row_to_post_full:tags 从 into_iter().filter().collect() 改 retain 原地过滤。unwrap_or_default() 已返回 owned Vec,旧写法重建第二个 Vec。每行文章记录省一次 Vec 分配。 顺带修 slugify 一个 dead store(prev_dash=false 后被 true 无条件覆盖)。 --- src/api/posts/helpers.rs | 20 ++++++-------------- src/api/posts/list.rs | 25 +++++-------------------- src/api/posts/search.rs | 5 +---- src/api/slug.rs | 2 +- 4 files changed, 13 insertions(+), 39 deletions(-) diff --git a/src/api/posts/helpers.rs b/src/api/posts/helpers.rs index 117ea9d..69a45a2 100644 --- a/src/api/posts/helpers.rs +++ b/src/api/posts/helpers.rs @@ -24,13 +24,9 @@ pub(super) fn row_to_post_list_item(row: &tokio_postgres::Row) -> PostListItem { let status_str: String = row.get("status"); let status = PostStatus::from_str(&status_str).unwrap_or(PostStatus::Draft); - // 聚合标签并过滤空字符串。 - let tags: Vec = row - .try_get::<_, Vec>("tags") - .unwrap_or_default() - .into_iter() - .filter(|t| !t.is_empty()) - .collect(); + // 聚合标签并原地过滤空字符串(retain 避免 into_iter+filter+collect 的二次 Vec 分配)。 + let mut tags: Vec = row.try_get::<_, Vec>("tags").unwrap_or_default(); + tags.retain(|t| !t.is_empty()); let word_count: i32 = row.get("word_count"); let reading_time: i32 = row.get("reading_time"); @@ -66,13 +62,9 @@ pub(super) async fn row_to_post_full( let role_str: String = row.get("status"); let status = PostStatus::from_str(&role_str).unwrap_or(PostStatus::Draft); - // 聚合标签并过滤空字符串。 - let tags: Vec = row - .try_get::<_, Vec>("tags") - .unwrap_or_default() - .into_iter() - .filter(|t| !t.is_empty()) - .collect(); + // 聚合标签并原地过滤空字符串(retain 避免 into_iter+filter+collect 的二次 Vec 分配)。 + let mut tags: Vec = row.try_get::<_, Vec>("tags").unwrap_or_default(); + tags.retain(|t| !t.is_empty()); // 解析上一篇文章导航。 let prev_post = if let Ok(prev_title) = row.try_get::<_, String>("prev_title") { diff --git a/src/api/posts/list.rs b/src/api/posts/list.rs index 858fa4b..4246406 100644 --- a/src/api/posts/list.rs +++ b/src/api/posts/list.rs @@ -98,10 +98,7 @@ pub async fn list_published_posts( .await .map_err(AppError::query)?; - let mut posts = Vec::new(); - for row in &rows { - posts.push(row_to_post_list_item(row)); - } + let posts: Vec<_> = rows.iter().map(row_to_post_list_item).collect(); crate::cache::set_post_list(&cache_key, posts.clone(), total).await; Ok(PostListResponse { posts, total }) @@ -156,10 +153,7 @@ pub async fn list_posts(page: i32, per_page: i32) -> Result = rows.iter().map(row_to_post_list_item).collect(); Ok(PostListResponse { posts, total }) } @@ -219,10 +213,7 @@ pub async fn list_deleted_posts( .await .map_err(AppError::query)?; - let mut posts = Vec::new(); - for row in &rows { - posts.push(row_to_post_list_item(row)); - } + let posts: Vec<_> = rows.iter().map(row_to_post_list_item).collect(); Ok(PostListResponse { posts, total }) } @@ -312,10 +303,7 @@ pub async fn get_posts_by_tag( .await .map_err(AppError::query)?; - let mut posts = Vec::new(); - for row in &rows { - posts.push(row_to_post_list_item(row)); - } + let posts: Vec<_> = rows.iter().map(row_to_post_list_item).collect(); crate::cache::set_posts_by_tag_paged(&cache_key, posts.clone(), total).await; Ok(PostListResponse { posts, total }) @@ -364,10 +352,7 @@ pub async fn get_posts_by_tag( .await .map_err(AppError::query)?; - let mut posts = Vec::new(); - for row in &rows { - posts.push(row_to_post_list_item(row)); - } + let posts: Vec<_> = rows.iter().map(row_to_post_list_item).collect(); // total 为真实 COUNT(*),不再用 posts.len()。 crate::cache::set_posts_by_tag(&tag_name, posts.clone(), total).await; diff --git a/src/api/posts/search.rs b/src/api/posts/search.rs index b2250b6..1da35af 100644 --- a/src/api/posts/search.rs +++ b/src/api/posts/search.rs @@ -85,10 +85,7 @@ pub async fn search_posts(query: String) -> Result = rows.iter().map(row_to_post_list_item).collect(); let total = posts.len() as i64; cache::set_search_results(&cache_key, posts.clone(), total).await; diff --git a/src/api/slug.rs b/src/api/slug.rs index 94f6e06..73e1e20 100644 --- a/src/api/slug.rs +++ b/src/api/slug.rs @@ -50,8 +50,8 @@ pub fn slugify(title: &str) -> String { break; } } - prev_dash = false; // 汉字成词,后接 `-`(合并连续、去除尾部由末尾清理负责)。 + // 拼音后必然接 `-`,所以 prev_dash 在此无条件置 true,中间无读。 if !push_char(&mut out, &mut len, '-') { break; }