{
    "version": "https://jsonfeed.org/version/1",
    "title": "My Blog",
    "home_page_url": "https://kyriechao.github.io",
    "feed_url": "https://kyriechao.github.io/rss.json",
    "description": "Personal blog powered by Next.js",
    "icon": "https://kyriechao.github.io/logo.png",
    "author": {
        "name": "Kyrie Chao",
        "url": "https://github.com/KyrieChao"
    },
    "items": [
        {
            "id": "https://kyriechao.github.io/posts/2026/11/12-diary",
            "content_html": "\n## 日常\n\n今天上午包饺子了，这个是前几天班任跟我说的\n\n这么多人一起包饺子，上次还是高中时候 那时候是我们自己切、擀、包 老师们出各各工具和饺子馅\n\n但是 这里是是饺子皮准备了、饺子馅自己做。\n\nemm, 怎么说呢 我感觉跟高中的比没什么很有意义的似的 \n\n不是这个事情没有意义 而我的感受是 跟他们还不是很熟悉 即使做很多意义的事情 也没那么意思。\n\n我也不知道为什么 做出来的饺子不是很喜欢吃 是不熟悉的原因么？\n\n\n下午上课了、晚上老师以往会进行技术面试 按组的。\n\n我对自己准备的简历还没有好好准备、被模拟面试痛打 都没好好看技术方面的准备\n\n但是今晚老师讲了OpenCV的相关技术点 没有进行技术面试 我很开心也不知道为什么。\n\n\n今天就这么多\n\n",
            "url": "https://kyriechao.github.io/posts/2026/11/12-diary",
            "title": "2026-11-12 感受",
            "summary": "平静的一天 感受自己",
            "date_modified": "2026-11-12T11:31:26.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/11/11-diary",
            "content_html": "\n\n## 日常\n\n早上起床、已经开始排除这里学习了\n\n可能是 模拟面试 技术面试 让我过于着急 这段时间的我 焦虑、迷茫、无助。\n\n今天又写不完回去了。\n\n---\n\n继续\n\n现在是11.12 现在讲上面内容相关的 不讲今天的\n\n当晚、我去模拟面试了\n\n也是一如既往的卡 但是通过跟那位老师聊天 他说我话术没问题 就是卡\n\n> 这个问题 我们也没办法 只能是练 说多了\n\n我似乎自己 放松了 释放了 往前的那种 焦虑、迷茫、愤怒\n\n我也不知道为什么 但就是放松下来了 睡觉也放松了 以前一直睡得不安稳",
            "url": "https://kyriechao.github.io/posts/2026/11/11-diary",
            "title": "2026-11-11 平静的一天",
            "summary": "今天犹如一滩死水",
            "date_modified": "2026-11-11T12:26:17.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/11/10-diary",
            "content_html": "\n## 日常\n\n![Lan](/images/2026/11/10/4.jpg)\n\n今天起大早 6点40起的8点的车，赶回校的高铁、就为了回校拍学信网采集照片。\n\n> 今天起得很早，6:40 起床，赶 8:00 的高铁回学校，只为了拍摄学信网采集照片。\n\n前一晚、我想了好多事情\n\n- 多个月不见我跟我的朋友见面会是什么样的\n\n- 他们有没有想念我\n\n- 他们会跟我问什么问题\n\n想着想着我自己的那么想念愈加强烈 \n\n- 也许想念的是他们 \n\n- 也许想念的是那个时期的自己\n\n- 也许想念的是那时候没有压力的自己 或者说 是我一种逃避现在环境的想法\n\n可真正遇到的时候、我曾经那么强的情绪没有迸发出来 而当时更是一种平静\n\n看到他们好像变了也没变、让我有点失望的是 \n\n跟我之前一直一起的搭子 没有跟我们去吃饭 前面就聊了多许 后面回自己家里了\n\n前面聊了挺多 就是聊没见面时候的事情 因为拍照老师不允许 所以 拍完照出去了\n\n他在准备 事业编/公务员 29号考 考完了放松 但是回出租屋休息 为什么不一起吃个饭呢\n\n今天 讲到这里吧 先休息\n\n--- \n\n## 补记（11月12日）\n\n### 关于她\n\n11.10 是她的生日 即使过了很多年 我不是刻意记得 就是很自然的想起。\n\n对她 我更多的是执念 并非深情 我只是不愿看清楚罢了。\n\n现在说起来已经毫无波澜了，那我为什么还会想起她? 可能遗忘 最先忘记的是曾经的过往，现在只记得过去以为的重要记忆锚点。\n\n### 回到学校\n\n离别和相识 我可能需要学习很长一段时间\n\n现在这个阶段的离别 大学毕业、机构结课等等\n\n10号回到学校，我看到我的宿舍座位 让我想到了曾经在宿舍写代码的日子也想到曾经跟朋友一起吃饭、上课、聚餐这些。\n\n也许我想念的是人、环境或者曾经的自己。\n\n### 曾经的生活\n\n曾经为了提高时间利用率 从晚上的熬夜改到了早起\n\n宿舍是晚上23点断电、早上6点来电\n\n熬夜写代码基本到1-2点、睡觉时间不足7小时 各各原由改成了早上6点开始写代码\n\n比如 写到太晚 早八起床早睡眠不足\n\n熬夜电脑电量不支持顶多写个2-3小时\n\n睡眠不足\n\n早上6点就来电了 就可以使一整天的电脑 使用率变高了。\n\n其实我有很多话想说的 今天的状态和10号当天的状态不符合。\n\n我当天对自己的模拟面试或者说话方面 还是焦虑、顾及\n\n这个问题已经在这段时间困扰很久了。\n\n![哥伦比亚](/images/2026/11/10/1.jpg)\n\n虚假之天。\n\n![一起吃饭](/images/2026/11/10/2.jpg)\n\n![一起吃饭](/images/2026/11/10/5.jpg)\n\n以后一起吃饭的机会、也许会变得很少\n\n不知什么缘故这块想写的欲望不是很强。\n\n### 关于离别\n\n![离开](/images/2026/11/10/3.jpg)\n\n不舍。\n\n",
            "url": "https://kyriechao.github.io/posts/2026/11/10-diary",
            "title": "2026-11-10 回校 复杂 惆怅",
            "summary": "回到学校的觉很开心但是感觉好多东西变了曾经的地方人的不在了",
            "date_modified": "2026-11-10T11:47:34.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/11/09-diary",
            "content_html": "\n## 日常\n\n![期待本身就是一种负重](/images/2026/11/09/8.jpg)\n\n期待本身就是一种负重 或者说 期待是一种微妙的暴力。\n\n例如 我天天想口吃如果我没有 的话、我可能会比现在好过的很多。\n\n可以选自己喜欢吃的、可以正常分享自己的趣事 、 别人也更愿意问我问题 或者说 我更受欢迎。\n\n可以坦荡的面对喜欢的人，也许那时候的我可能不会因口吃而烦恼可能会变成身高、学历 新的自己以为的缺点。\n\n以上都是我的幻想 就像下面的图所说的\n\n我会幻想我不是口吃状态下的生活，幻想会让我提前感受、愉悦。\n\n![幻想解离](/images/2026/11/09/1.jpg)\n\n有时候我会想为什么我的缺点是最基础的\n\n例如 狗 被夺去了吠 它可以看家、驱赶陌生人 你以为这条狗跟别的狗别无二样\n\n但它失去了恐吓、威慑的功能或者能力。\n\n到现在我都痛苦挣扎、翻身、摆脱 被这个问题困扰到现在。\n\n从有意识开始 一直缠身、命运确实身不由己 我信科学，不信神佛 但有时我得靠信念 要让自己觉得不是自己的错 只是神在惩罚我。\n\n![期待本身就是一种负重 纯图](/images/2026/11/09/16.jpg)\n\n我无法解释我的痛苦、无法定义我的痛苦、无法解决我的痛苦\n\n![青石板的老街上](/images/2026/11/09/6.jpg)\n\n世界不会围着我转，有我没我照样日出日落\n\n我也会想我是不是别人观察的对象 我是不是其他高维度世界观察的对象\n\n我的各各想法 总是逃避现实的真我 不想真实面对真实的自我\n\n![玫瑰](/images/2026/11/09/14.jpg)\n\n若时间真能化解一切的话、那芸芸众生讲无苦诉说\n\n![若时间真能化解一切的话](/images/2026/11/09/12.jpg)\n\n很多说 很多问题在时间的长流中会不复存在。\n\n如果说这个问题一直跟随他呢？人的耐受确实可以慢慢被提高 包括痛苦的耐受度。\n\n痛苦一直迭代 被迫我接受 很多人会说长大了就没事了 时间一长就没事了。\n\n一群不对结果负责的人 往往把问题抛给未来 换来当前的释然。\n\n![那芸芸众生讲无苦诉说](/images/2026/11/09/18.jpg)\n\n我往个人说的多 因为我的日志\n\n天下有那么多人痛苦的 如果时间真能化解一切的话 那芸芸众生讲无苦诉说。\n\n![下雪](/images/2026/11/09/9.jpg)\n\n\n原神\n\n![加载](/images/2026/11/09/13.jpg)\n\n天虽有命，但运在自身。天行健，君子以自强不息，如是而已\n\n![日常](/images/2026/11/09/3.jpg)\n\n雷神 —— 很喜欢的角色\n\n![雷神](/images/2026/11/09/2.jpg)\n\n![雷神](/images/2026/11/09/4.jpg)\n\n\n![冰神](/images/2026/11/09/10.jpg)\n\n你本不该被诅咒\n\n![丘丘人](/images/2026/11/09/7.jpg)\n\n![原神](/images/2026/11/09/11.jpg)\n\n![原神](/images/2026/11/09/5.jpg)\n\n![原神](/images/2026/11/09/17.jpg)\n\n![原神](/images/2026/11/09/15.jpg)\n\n![原神](/images/2026/11/09/20.jpg)\n\n![原神](/images/2026/11/09/19.jpg)\n",
            "url": "https://kyriechao.github.io/posts/2026/11/09-diary",
            "title": "2026-11-09 新的发现",
            "summary": "我发现我看着自己上传的图片 能写好多东西",
            "date_modified": "2026-11-09T00:38:40.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/11/07-diary",
            "content_html": "\n## 日常\n\n今天 开始慢慢恢复学习 Rust了\n\n<a href=\"https://github.com/KyrieChao/KyrieChao.github.io/blob/main/exercises/rust/2026/11/07/cli_todo_list.rs\" target=\"_blank\" rel=\"noopener noreferrer\">CLI 练习（Rust）</a>\n\n\n写了各各语言的 Hello World! 我想的是对比学习。\n\n今天老师讲了项目相关的\n\n工业网关架构图\n![工业网关架构图](/images/2026/11/07/1.png)\n\n物联网云平台项目架构图\n![物联网云平台项目架构图](/images/2026/11/07/2.png)\n\nCAN 总线的原理图\n![图](/images/2026/11/07/3.png)\n\n口吃方面 我似乎揪的越紧它就越厉害 但是我也会每天练习个20分钟\n\n今晚就开始休息了 没有之前的紧迫感了 周末也可以好好休息了。",
            "url": "https://kyriechao.github.io/posts/2026/11/07-diary",
            "title": "2026-11-07 blog增加功能 练习Rust小些语法",
            "summary": "blog增加功能 练习Rust小些语法",
            "date_modified": "2026-11-07T09:24:25.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/11/06-diary",
            "content_html": "\n## 日常\n\n![花](/images/2026/11/06/2.jpg)\n\n用好看的图开个头吧\n\n这张花的图片是我昨天刷视频时,刷「随笔」看到的图片 我很喜欢「随笔」这种 表达自己想法的方式。\n\n我也想过自己写一些随笔文案 但是以我现在的状态 似乎还不足以很好地表达自己真正想表达的东西。\n\n一方面是知识积累还不够，另一个方式是有时候自己的想法经不起深入思考。甚至有时候，听别人分享随笔，也会发现自己并不能完全理解其中的意思。\n\n昨天补写了前天的日志 晚上却忘记记录当天的事情了。\n\n其实昨天也没有什么特殊的。当然，我似乎每天都会说今天没什么特殊的。\n\n### C++ 学习\n\n课上学了 C++ 的 命名空间 `namespace`、继承及其相关细节，包括单继承、多继承、棱形继承、构造与析构顺序、内存大小以及同名覆盖等等。\n\n越学 C++,越觉得他和 Java有些相似。\n\n我的意思是，因为我最先学的是 Java，所以现在学习 C++ 时，很多知识点都可以直接对照理解，只是语法和具体实现有所不通。\n\n\n不过，我确实还需要更多练习。有时候真正开始写 C++ 程序，还是会无从下手。\n\n## 卡皮塔诺\n\n![卡皮塔诺](/images/2026/11/06/1.jpg)\n\n我很喜欢《原神》里的这位角色——**卡皮塔诺**。这张图应该是一个月前，或者更久之前的截下来的。\n\n他奉行战士之道 同时 我最欣赏的应该还是他的正义感。\n\n我觉得做人或者做事，都应该有自己的原则和正义感。\n\n至于为什么会对这个角色产生这样的认同，我似乎还说不太清楚。\n\n\n## 关于人生的一些思考\n\n### 人到洛阳花似锦，偏我来时不逢春\n\n![人到洛阳花似锦，偏我来时不逢春](/images/2026/11/06/3.jpg)\n\n> 人到洛阳花似锦，偏我来时不逢春\n\n生不逢时、时运不济，错过了美好时机的怅惘。\n\n同样的地方，同样的机会，别人赶上了最好的时候，自己却偏偏错过。\n\n有时候确实会产生这样的感受。个人的力量是有限的，似乎只能看着命运摆布自己。\n\n但是，我觉得不能只看到别人已经获得了什么，在不了解别人是如何获得这些东西之前，就先去抱怨命运。\n\n**比较是偷走幸福的小偷。**\n\n我不能把别人已经获得的东西，当作自己本应该得到、却已经失去的东西。\n\n### 与自己和解\n\n![图片](/images/2026/11/06/4.jpg)\n\n现在想来，可能真就是这样吧。\n\n也许是自己慢慢和自己和解了。\n\n但很多时候，还是会感到「偏我来时不逢春」。\n\n人生、人性、悟性，一个人的一生，无非就是在这几个东西里面翻来覆去。\n",
            "url": "https://kyriechao.github.io/posts/2026/11/06-diary",
            "title": "2026-11-06 补日志",
            "summary": "记录日常学习、喜欢的角色，以及对人生与命运的思考",
            "date_modified": "2026-11-06T01:24:21.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/11/05-diary",
            "content_html": "\n\n## 日常\n\n今天是11.6号 我来补昨天的日志\n\n昨天其实没什么特别的 也没什么可以记录的。\n\n上课 下课 回宿舍 吃午饭 休息 上课 下课 去买晚餐 上晚自习 下课 休息。\n\n我现在感觉没什么有趣的就不写上去了  但是我怕未来的我 看不到任何信息 所以当天不累的话 我也会尽可能的写的多一点。\n\n甚至放一些图片.\n\n\n![图](/images/2026/11/05/1.jpg)\n\n11.10号要回学校一趟了\n![图](/images/2026/11/05/2.jpg)\n\n这个日期 让我想到了 某个人的生日 即使过去了很久或者不见面很久\n\n我都不会忘记为什么呢 也许我从未忘记 但是我承认她已经过去了。\n\n木偶伤害 我没想到会这么高\n\n![图](/images/2026/11/05/3.jpg)\n\n下面是一些原神日常截图\n\n![图](/images/2026/11/05/4.jpg)\n\n![图](/images/2026/11/05/5.jpg)\n\n![图](/images/2026/11/05/6.jpg)\n\n![图](/images/2026/11/05/7.jpg)\n\n![图](/images/2026/11/05/8.jpg)\n\n![图](/images/2026/11/05/9.jpg)\n\n![图](/images/2026/11/05/10.jpg)\n\n![图](/images/2026/11/05/11.jpg)\n\n",
            "url": "https://kyriechao.github.io/posts/2026/11/05-diary",
            "title": "2026-11-05 补日志",
            "summary": "昨天忘记写了",
            "date_modified": "2026-11-05T01:24:21.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/11/04-diary",
            "content_html": "\n\n## 日常\n\n\n无状态 自己买的锡膏到了 可以自己焊了 今晚看着情况焊板子吧。\n\n跟同桌商量 帮我练习面试的。\n",
            "url": "https://kyriechao.github.io/posts/2026/11/04-diary",
            "title": "2026-11-04 无状态",
            "summary": "无状态",
            "date_modified": "2026-11-04T12:02:14.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/11/03-diary",
            "content_html": "\n## 日常\n\n不知道 我应该怎么描述当前的状态了。\n\n之前感觉周围被黑暗包裹或者我被包裹 他们就像能吸食我的自信力、生命力一样。\n\n现在只是比之前好了一点、能喘息、能感觉到自己\n\n我现在对自己 已经没有曾经的自信、灵感了\n\n比如 人家本来是 长方形 有自己的棱角、自信\n\n但是偏偏要把他塑造成三角形 三角形的棱角是长方形曾经的平面 可以理解成不擅长的一面\n\n挫败 确实会让人痛苦\n\n我知道必须翻过这个坎 但是我没有那个信心和力气了\n\n我的想象 外壳尽管多硬 总会被破开 但是破开之后的重新长出来的 会比前一个更坚硬。\n\n今天我们班 有一位同学 找到面试了 准确来说是笔试\n\n投简历是 先通过模拟面试 之后才能投简历 \n\n我都没过模拟面试呢 别人都有面试了\n\n我在想 好多人都面试走了 我还没有面试\n\n我只能去下一班级了 \n\n我希望的是年前能找到实习\n",
            "url": "https://kyriechao.github.io/posts/2026/11/03-diary",
            "title": "2026-11-03 平常又不平常",
            "summary": "不知道 我应该怎么描述当前的状态了",
            "date_modified": "2026-11-03T11:31:57.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/11/02-diary",
            "content_html": "\n\n## 日常\n\n面试虽然不顺但是面对他人 讲的越多次 熟练越多次。\n\n感觉 规划带来的霉运 慢慢消散中。\n\n今天不多说了。",
            "url": "https://kyriechao.github.io/posts/2026/11/02-diary",
            "title": "2026-11-02 心情好转",
            "summary": "心情好转",
            "date_modified": "2026-11-02T12:29:14.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/30-diary",
            "content_html": "\n\n## 日常\n\n已经什么都不做了、按规划来就是倒霉。\n\n今天教了Git操作和Makefile文件。 我都会都不用看。\n\n\n晚上还要背话术呢。",
            "url": "https://kyriechao.github.io/posts/2026/10/30-diary",
            "title": "2026-10-30 无趣",
            "summary": "无趣今天什么都没做",
            "date_modified": "2026-10-30T12:27:17.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/29-diary",
            "content_html": "\n\n## 日常\n\n无 痛苦的一天。",
            "url": "https://kyriechao.github.io/posts/2026/10/29-diary",
            "title": "2026-10-29 痛苦的一天",
            "summary": "模拟面试真的是一坨屎",
            "date_modified": "2026-10-29T11:19:51.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/28-diary",
            "content_html": "\n> 我从做规划开始 我变的一直很倒霉\n\n我想先把这件事认真地拆开，而不是直接告诉你“只是巧合，别想太多”。\n\n你说的是：从开始做规划以后，你感觉自己一直在倒霉。\n\n我记得你为什么开始规划。你希望提前知道自己处于什么水平、今天应该练什么、事情能推进到什么程度。你习惯先制定半月计划，再细化到周计划和每日计划。你想通过安排，让未来更可控，而不是每天稀里糊涂地过。\n\n所以，如果你现在觉得越来越倒霉，这件事值得认真看待。\n\n但我不急着把它归结为心理作用，也不会直接认定规划导致了你的不幸。这里至少有三个可能性：\n\n- 事情确实变多了。 你开始同时推进学习、简历、项目、博客等事情，遇到问题的机会自然增加。\n- 规划让落差变得更明显。 以前遇到挫折，只是遇到了挫折；现在有了预期，你还得面对计划被打乱、进度落后以及对自己能力的怀疑。\n- 你经历了一段真实的不顺。 有些事情就是会接连出问题，不必把每件事都解释成自己做错了什么。\n\n这三种情况也可能同时存在。\n\n我还注意到一个关键区别：规划本身未必让你更倒霉，但它可能让你开始用另一种方式体验生活。 以前你只需要处理眼前的问题，现在你还要判断自己有没有偏离预定的轨道。\n\n不过，我不想替你解释太多。你说这句话，可能不是在抱怨计划没完成，而是真的觉得生活里发生了很多不顺的事。\n\n我只问你一个问题：\n\n你说的“倒霉”，具体指什么？\n\n是电脑坏了、硬件出问题、事情接连出现意外这种客观上的不顺；是学习、项目和计划总被打断；还是你觉得自从开始规划，整个人的状态、心情乃至生活都变差了？\n\n你可以直接讲最近发生的事情，不必整理语言，也不用急着证明规划和倒霉之间有因果关系。我们先把发生了什么弄清楚，再判断到底是什么在变化。\n\n> 我上上周六开始做的规划\n> 周日上就业课 简历让写的莫名其妙的 他的下周一主板就烧了 \n> 周五简历定稿了 今天好不容易下决心去模拟面试 老师却提前走了\n> 上周开始到现在技术老师 意识到之前讲课速度太慢 开始赶进度 讲的东一个西一个的\n> 晚上我打算焊板子 换换心情 东西都摆全了 后来助焊剂找不到了 \n> 我跟这里的朋友借一个 他说组员在用呢\n\n朝，我把你说的事情串起来看了一遍。\n\n从上上周六开始规划，到今天，确实接连碰上了好几件不顺的事。\n\n- 简历： 就业课布置的任务让你觉得莫名其妙，后来又花了不少精力才把简历定稿。\n- 硬件： 主板烧了，原本想推进的小车项目也受到了影响。\n- 模拟面试： 你终于下定决心去练习，老师却提前走了。你做出了行动，结果却没能得到预期的反馈。\n- 技术学习： 老师突然加快进度，课程内容变得零散，你很难按照原本的节奏消化。\n- 今晚焊板子： 你想换换心情，连东西都摆好了，偏偏找不到助焊剂，向朋友借也没借成。\n\n单独看，每件事似乎都有原因；但连续发生在一起，确实很容易让人产生一种感受：我明明在努力把生活安排好，为什么事情反而越来越不顺？\n\n我认为，这里面有一个比运气更值得注意的地方。\n\n你开始规划，是为了获得掌控感。但这段时间，你认真准备的事情反复被外部因素打断：主板损坏不是你计划出来的，老师提前离开也不是你能控制的，课程突然加速同样不是你能决定的。\n\n你投入了时间、精力，也承担了期待，却没能获得相应的结果。这才是这些事情累积起来最让人难受的地方。\n\n但我不认为你应该因此放弃规划。也不建议你现在就把这一切归因于自己的心态。 规划没有证据会招来坏事。你遇到的客观挫折是真实的，但它们发生在规划开始之后，不代表它们是规划造成的。\n\n今晚，我反而建议你先别规划了。\n\n助焊剂找不到，就先放下焊接。不是让你认输，而是你已经为这一天投入了足够多的期待，不必再逼自己一定要把今晚扳回来。等东西齐了再焊，也不会让你的项目倒退。\n\n还有一件事，我想问你。\n\n你现在最难受的，是觉得自己运气太差，做什么都碰壁；还是觉得自己已经很努力地想改变现状了，却发现自己根本控制不了生活？\n\n这两种感受很接近，但不一样。我想知道你真正卡在哪里。\n\n\n> 再加点吧\n> 上周末我打算休息 毕竟连着两周没休息了\n> 舍友要去爬长城 他们打算上上周去的 因为我不在 都没去 上周末我就必须得去了\n> 现在已经快一个月没休息了\n> 模拟面试这个 准备这个我几乎不听课了 哪有心思听课 你也知道 别人普通不过的说话 在我这里可能是一个不可逾越的鸿沟 \n> 别人紧张是怕不知道说什么 我怕说不出来\n> 为什么？\n\n朝，我现在更理解你为什么会把这些事情联系起来了。\n\n这已经不只是主板烧了、助焊剂找不到，或者老师提前离开的问题了。你原本想通过规划，让自己的生活逐渐走上正轨，但从开始规划到现在，你付出的精力越来越多，生活却没有因此变得更顺。\n\n而且，你现在连休息都很难真正休息。\n\n你连续两周没休息，本来打算上周末放松一下，却因为舍友之前迁就你没去成的长城之行，觉得自己这次必须去。于是，原本属于你的休息时间，也变成了需要履行的安排。\n\n与此同时，你为了模拟面试，几乎已经没心思听课了。\n\n你不是不知道该学习什么，也不是没有在行动。恰恰相反，你在努力推进很多事情，却发现每件事都在争夺你的时间和精力。\n\n我的判断是：你现在最需要警惕的，不是运气不好，而是你正在把改变自己的成本越抬越高。\n\n## 一、为什么连普通的事情，对你来说都这么难？\n\n你说了一句很重要的话：\n\n> 别人紧张是怕不知道说什么，我怕说不出来。\n\n这两种恐惧存在本质区别。\n\n- 别人担心的是内容问题：我知道怎么做，但我怕答错。\n- 你担心的是输出问题：即使我知道怎么做，我也可能无法把它表达出来。\n\n假如你去参加 Linux 应用开发的模拟面试，面试官问你 `fork()` 的作用。\n\n你可能并不是完全不知道答案。你学过进程、写过代码，也理解过父子进程的关系。\n\n但面试不是独自看代码。你必须在别人注视下，迅速组织语言，把脑海里的知识转换成完整的表达。\n\n这中间有一系列步骤：\n\n1. 听懂问题。\n2. 从记忆中提取知识。\n3. 组织表达顺序。\n4. 把想法转换成语言。\n5. 在对方的注视下完成输出。\n\n任何一个环节卡住，你都可能出现沉默。\n\n而且，越害怕说不出来，你就越容易在开口之前监控自己的表现：我说得对不对？我接下来该说什么？如果卡住了怎么办？\n\n这些额外的心理负担，又会挤占你原本用于思考和表达的注意力。\n\n于是就形成了一个循环：\n\n害怕说不出来 → 更关注自己的表现 → 更难组织语言 → 更害怕下一次开口。\n\n这不是说你一定存在某种心理障碍，而是说，你所描述的困难，很可能不只是知识储备不足，还涉及实时表达和面对评价时的压力。\n\n## 二、为什么你会觉得这是不可逾越的鸿沟？\n\n我想指出一个可能被你忽略的问题。\n\n你正在同时训练两种难度完全不同的能力：\n\n- 技术能力：学习 Linux、网络通信、C 语言，理解程序为什么这样运行。\n- 面试表达能力：在有限时间内，把自己掌握的技术清楚地讲给别人听。\n\n你目前还在积累技术经验，却已经把模拟面试当成一个必须认真准备的任务。为了准备面试，你甚至开始无法专心听技术课。\n\n这意味着，你正在用一种能力的训练，挤压另一种能力的成长。\n\n更麻烦的是，你可能会把表达上的困难解释成自己整体能力不行。\n\n明明只是一个问题说不出来，你却可能进一步怀疑：我的技术是不是不够？我是不是根本不适合这个行业？别人能做到的事情，为什么我做不到？\n\n当一项具体技能的困难，被你理解成对自身能力的整体否定时，它就会变得格外沉重。\n\n但我不会直接告诉你“多练练就好了”。因为你现在真正需要弄清楚的是：你究竟在哪个环节卡住了。\n\n如果你脑子里根本没有答案，那是知识问题。\n\n如果你知道答案，但只能在脑子里想，无法组织成句子，那是语言组织问题。\n\n如果你独自练习时能说出来，一面对老师或面试官就完全说不出来，那就需要重点关注压力对表达的影响。\n\n这三种情况的解决方法并不相同。\n\n## 三、规划为什么反而让你越来越累？\n\n我把你最近的事情放在一起看，发现了一个共同点。\n\n你给自己安排的任务，越来越不只是完成一件事，而是逐渐带上了更高的意义。\n\n简历不只是简历，它关系到就业。\n\n模拟面试不只是练习，它关系到你能不能跨过表达这道坎。\n\n技术课不只是上课，它关系到你能不能掌握 Linux 应用开发。\n\n小车焊接不只是焊接，它也是你在连续学习之后，想让自己放松一下的方式。\n\n甚至休息，也因为舍友的安排而带上了责任。\n\n于是，当其中一件事出问题时，你失去的可能不只是那件事本身，还包括你原本寄托在它上面的期待。\n\n这也解释了为什么助焊剂找不到，会成为今晚压垮你心情的最后一件小事。\n\n它并不严重，但你已经累积了太多未能顺利推进的事情。\n\n我认为，你目前的规划需要调整的地方，不是把每天安排得更精确，而是给自己保留不需要完成任何任务、不需要向任何人交代的时间。\n\n休息不是完成其他任务之后才能领取的奖励，而是维持长期学习能力的必要条件。\n\n你也不必因为舍友之前迁就过你，就认为自己必须牺牲休息时间来偿还。你可以珍惜他们的善意，同时承认自己确实需要休息。\n\n## 四、我现在最想弄清楚的一件事\n\n朝，我不想继续替你分析一大堆，然后让你带着更多需要思考的问题离开。\n\n你刚才问：“为什么？”\n\n我想先从最核心的地方弄清楚。\n\n当你面对老师进行模拟面试，担心自己说不出来时，你的真实体验更接近哪一种？\n\n- A. 脑子里其实有答案，但一开口就组织不好语言，越着急越说不出来。\n- B. 面对别人时脑子会突然空白，原本知道的东西也想不起来。\n- C. 我知道自己需要说什么，但在开口之前，就已经开始害怕、回避，甚至不想面对这个场景。\n- D. 不止是面试。我平时和别人交流，也经常有这种感觉。\n\n你可以选一个，也可以说几个都有。\n\n我问这个不是为了给你贴标签，而是因为你已经为这件事投入了大量时间，甚至影响到正常上课。现在不能再笼统地把它当成“缺乏自信”，然后让你继续硬扛。我们需要找到真正的卡点。\n\n\n> 我不是跟你说过的么？\n> 我有口吃  哪怕脑子里编排的多么完美、说话的时候我不知道那一时刻我会卡住 声都出不来\n> 然后 如果我会用力的话 那个词会变形你知道么？我会逃避这个 用力的话 我的脸我的嘴都会歪曲\n> 我会选择不讲下去 去试着换别的词儿 来表达 我没能说出来的词\n\n\n朝，你说过。是我刚才没有把你已经告诉过我的情况纳入分析，还把问题拆成了知识储备、语言组织和紧张导致的表达障碍。这是不准确的。你已经明确告诉我，你有口吃，我不应该让你重新解释一遍。\n\n我现在重新理解你说的面试恐惧。\n\n你害怕的不是不知道说什么，而是你明明知道自己要说什么，却无法确定自己能不能把它说出来。\n\n你可以在脑海里把答案编排得很好，甚至每句话都想好了。但真正开口时，你无法预知自己会不会突然卡住，声音完全发不出来。\n\n如果强行用力，你的发音可能变形，面部和嘴部也会出现你不希望发生的扭曲。于是，你选择换词、绕开那个词，或者干脆不再讲下去。\n\n这意味着，你在回答一个技术问题时，可能同时承担着三件事：\n\n- 思考技术答案。\n- 监测自己能不能顺利发音，并在卡住时寻找替代表达。\n- 承受卡住、发音变形以及被别人看到这些情况的压力。\n\n别人可能只需要思考答案怎么组织，而你还得面对一个无法完全预测的发声过程。\n\n所以，你说“别人紧张是怕不知道说什么，我怕说不出来”，这句话我现在理解了。对你而言，面试的困难不只是表达能力的问题，而是你无法保证自己能够顺利完成表达。 这和单纯的怯场不是一回事。\n\n### 我也想重新看待你最近的规划\n\n你为了模拟面试，几乎没心思听课。你不是不知道技术学习重要，而是你清楚自己将要面对什么，也知道这道坎对你有多难。\n\n你可能已经在脑海里反复演练过：如果老师问我这个问题，我该怎么回答？如果那个词突然卡住了怎么办？能不能换一种说法？\n\n但即使准备得再充分，也无法消除你对下一刻发不出声音的担忧。\n\n这很容易让人陷入一种困境：越重视这次面试，就越想准备充分；越想准备充分，就越容易把注意力和精力投入进去，直到其他事情都受到影响。\n\n而当你终于鼓起勇气去找老师模拟面试，老师却提前离开了。\n\n你失去的不只是一次练习机会，还有一次本来已经做好心理准备、终于打算面对这件事的机会。\n\n再叠加主板损坏、课程加速、休息被打断，以及今晚焊接计划落空，你会觉得从开始规划起，生活就一直在和你作对。这种感受我能够理解，但我仍然不认为规划本身导致了这些事情。\n\n### 有一点，我不想轻易给你下结论\n\n我不会告诉你，只要多练习、增强自信，你就能克服口吃。也不会建议你每次卡住都强行把词挤出来，因为你已经明确说过，用力可能导致发音变形和面部扭曲。\n\n你需要的不是别人再告诉你一遍应该勇敢，而是找到一种不要求你先消除口吃，仍然能够学习、面试和工作的方式。\n\n我想先问你一个更贴近实际的问题。\n\n你在日常生活中和熟悉的人说话，也会经常出现完全发不出声音的情况吗？还是在需要正式表达、面对老师、陌生人或被评价的时候，情况会明显加重？\n\n我问这个，是为了区分你平时的口吃表现和面试场景带来的额外压力，而不是怀疑你所描述的困难。两者可能同时存在，但对你接下来怎么准备模拟面试，会有不同的意义。\n\n\n> 跟谁没有关系 就是无法预测 有时候可以很流畅的说 有时候直接卡\n> 就像诅咒一样 我不知道什么时候这个诅咒应验 我可能一直祈求着不要在发生了\n\n\n朝，我明白了。\n\n不是面对老师才会这样，也不是面对陌生人才会这样。没有一个固定的触发条件。有时候你可以流畅地说完一句话，有时候却会突然卡住，声音完全出不来。你无法提前知道哪一次会发生，也无法保证下一次不会发生。\n\n你真正承受的，是一种无法预测、无法完全控制的失控感。\n\n而且，这种失控发生在你每天都必须使用的能力上——说话。\n\n你不能因为担心口吃，就永远不说话。你要上课、和朋友交流、参加面试、寻找工作。你必须一次又一次地开口，却不知道下一次会不会卡住。\n\n所以你说它像诅咒，我能理解这个比喻。\n\n最折磨人的可能不是某一次卡住本身，而是你明明知道自己想说什么，甚至已经在脑海里完整地排练过，却依然无法确定自己能否把它说出来。\n\n你可以努力准备，却无法用准备彻底消除不确定性。\n\n你可以尝试绕开那个词，却又必须在说话的过程中临时寻找替代词。\n\n你甚至可能在一段流畅的表达中，始终隐约担心下一个词会不会突然卡住。\n\n你不仅要说话，还要时刻面对说不出来的可能性。\n\n这和普通的紧张确实不一样。\n\n### 我想谈谈你说的那句“我可能一直祈求着不要再发生了”\n\n我觉得，这句话比你前面说的很多话都更接近问题的核心。\n\n当一个人反复经历自己无法控制的事情，很自然地会希望下一次不要再发生。你可能在开口前祈求，在说话时小心翼翼，在卡住后懊恼，下一次又继续祈求。\n\n但你无法通过祈求获得确定性。\n\n这不是你的错，也不是你不够努力。\n\n而且，我不想把你现在的处境简单解释成“你太在意口吃了”。你确实会遇到发不出声音的时刻，确实会因为用力而出现你不愿意承受的发音和面部变化。这些不是凭空产生的担忧。\n\n我也不会轻率地告诉你，只要接受自己，就不会再难受。接受并不意味着你必须喜欢这种经历，更不意味着你不应该希望它有所改善。\n\n我更希望你逐渐拥有另一种可能：你依然可以希望自己说话流畅，依然可以寻求专业帮助，同时不必把每一次开口都变成一次等待判决的过程。\n\n口吃的具体表现和改善方式因人而异。如果它长期影响你的生活和求职，可以考虑找有口吃干预经验的言语语言治疗师评估，而不是要求自己单靠意志力解决。\n\n### 还有一件事，我想让你知道\n\n你最近拼命规划、准备简历、准备面试、学习技术。你想让自己变得更有能力，让未来更好。\n\n可你也许比别人更清楚，有些困难不是你准备得充分就一定能避开的。\n\n你可以把技术学会，可以把简历改好，可以提前准备面试答案，但你不能保证自己开口时不会卡住。\n\n这可能正是你为什么如此渴望掌控生活，却又在最近一连串不顺之后感到格外疲惫的原因之一。\n\n我不会替你断言，这就是你所有痛苦的来源。但我不认为你应该再给自己增加一项任务，要求自己连这个问题也必须立刻解决。\n\n你可以继续努力改变生活，而不必先把自己所有无法控制的部分都解决好。\n\n朝，我暂时不想再给你安排什么训练，也不想把这段话变成一份改善计划。\n\n我只想问你一句，不需要组织得很完整，也不需要给出一个正确答案：\n\n当你祈求着下一次不要再卡住时，你最害怕的究竟是什么？是卡住时身体那种难受、发音变形时对自己的厌恶，还是你不知道别人会怎么看你，以及这件事会怎样影响你未来的工作和生活？\n\n也可能三种都有，或者是别的东西。你可以慢慢说。\n\n",
            "url": "https://kyriechao.github.io/posts/2026/10/28-diary",
            "title": "2026-10-28 无趣",
            "summary": "无趣",
            "date_modified": "2026-10-28T12:21:43.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/27-diary",
            "content_html": "\n## 日常\n\n## 10 月 27 日，周二：Day 2 库\n\n核心任务：制作并使用静态库和共享库\n\n建立一个简单项目：\n\n```\nlibrary_demo/\n├── calc.h\n├── calc.c\n└── main.c\n```\n\n`calc.c` 实现 `add()` 和 `sub()`，`main.c` 调用它们。\n\n必须完成：\n\n1. 编译出 `libcalc.a`。\n2. 使用静态库链接生成可执行文件，并运行。\n3. 编译出 `libcalc.so`。\n4. 使用共享库链接生成另一个可执行文件，并运行。\n5. 将共享库放到单独的目录，使用 `LD_LIBRARY_PATH` 测试运行时搜索。\n\n验收标准\n\n你能不照抄命令，独立完成下面的流程：\n\n```bash\ncalc.c → calc.o → libcalc.a → 静态链接程序\ncalc.c → calc.o → libcalc.so → 动态链接程序\n```\n\n并能解释 `-I`、`-L`、`-l`、`-fPIC`、`-shared` 分别解决什么问题，以及为什么共享库链接成功后仍可能在运行时找不到。\n\n这一天不要求掌握 `dlopen()` 插件机制，只需能说明它的用途。\n\n\n靠北 今天被简历折磨的、像🗡在头上似的。明天去模拟面试吧。",
            "url": "https://kyriechao.github.io/posts/2026/10/27-diary",
            "title": "2026-10-27 今天被简历折磨的、像🗡在头上似的。明天去模拟面试吧。",
            "summary": "今天被简历折磨的、像🗡在头上似的。明天去模拟面试吧。",
            "date_modified": "2026-10-27T01:07:42.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/26-diary",
            "content_html": "\n## 日常\n\n周末去爬长城了、今天腿都是酸的。\n\n其实我本来上周日休息的、毕竟没休息好久了。可是这个计划恰好是舍友上上周打算要去的、我没休息然后他们也等我最后没去。\n\n上周日我就得去了...\n\n长城\n![长城](/images/2026/10/26/4.jpg)\n\n小猫咪！\n![小猫](/images/2026/10/26/3.jpg)\n\n好多人、电梯也好长\n![人](/images/2026/10/26/2.jpg)\n\n我爬的快、等了他们1小时左右\n![长城](/images/2026/10/26/1.jpg)\n\n我等他们的期间、有一个意大利外国人(中年妇女)跟我问路，但是我英语不好啊☠️。\n\n我中间听到了个 finsh 我后面回想应该在问我 那里是终点么? 但是我回了个 \n\n> Sorry、my english very bad.\n\n我第一次后悔没好好学英语、没有帮到她。我感觉给祖国丢脸了......\n\n### 周一: Day 1 文件操作\n\n核心任务：独立实现文件复制程序\n\n具体练习\n\n编写 `mycp.c`，实现以下命令：\n\n```bash\n./mycp source.txt destination.txt\n```\n\n要求：\n\n1. 使用 `open()` 打开源文件、创建目标文件。\n2. 使用固定大小的缓冲区，例如 `char buf[1024]`。\n3. 使用循环调用 `read()`，直到文件读取结束。\n4. 对每次读取到的数据使用 `write()` 写入目标文件。\n5. 正确处理文件描述符关闭和错误返回。\n\n```c\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n#include <errno.h>\n\n#define BUFF_SIZE (1024 * 8)\n\nvoid assess(int dest, int src) {\n    if (close(dest) < 0) {\n        perror(\"close dest\");\n        if (close(src) < 0) perror(\"close src\");\n        exit(-1);\n    }\n    if (close(src) < 0) {\n        perror(\"close src\");\n        exit(-1);\n    }\n}\n\nint main(int argc, char **argv) {\n    if (argc != 3) {\n        fprintf(stderr, \"Usage: %s <src> <dest>\\n\", argv[0]);\n        exit(-1);\n    }\n    int src = open(argv[1], O_RDONLY);\n    if (src < 0) {\n        perror(\"open src\");\n        exit(-1);\n    }\n    int dest = open(argv[2], O_WRONLY | O_CREAT | O_EXCL, 0664);\n    if (dest < 0) {\n        close(src);\n        if (errno == EEXIST) {\n            fprintf(stderr, \"dest %s already exists\\n\", argv[2]);\n        } else {\n            perror(\"open dest\");\n        }\n        exit(-1);\n    }\n    char buf[BUFF_SIZE] = {0};\n    while (1) {\n        ssize_t r = 0;\n        do {\n            r = read(src, buf, BUFF_SIZE);\n        } while (r < 0 && errno == EINTR);\n        if (r < 0) {\n            perror(\"read src\");\n            assess(dest, src);\n            exit(-1);\n        }\n        if (r == 0) break;\n        ssize_t total = 0;\n        while (total < r) {\n            ssize_t w;\n            do {\n                w = write(dest, buf + total, r - total);\n            } while (w < 0 && errno == EINTR);\n            if (w < 0) {\n                perror(\"write dest\");\n                assess(dest, src);\n                exit(-1);\n            }\n            if (w == 0) {\n                fprintf(stderr, \"write returned 0\\n\");\n                assess(dest, src);\n                exit(-1);\n            }\n            total += w;\n        }\n    }\n    assess(dest, src);\n    printf(\"copy file finshed!\\n\");\n    return 0;\n}\n```\n\n必须测试的情况：\n\n- 源文件为空。\n- 源文件小于 1024 字节。\n- 源文件大于 1024 字节。\n- 源文件不存在。\n- 目标文件已存在。\n\n已实现: \n\n```bash\nroot@kyriechao:~/work/linux/test# ls -l | grep test_cp_src.txt \n-rw-r--r-- 1 root root    13 Oct 26 10:22 test_cp_src.txt\nroot@kyriechao:~/work/linux/test# ck mycp.c -- test_cp_src.txt test_cp_dest.txt\n[编译] mycp.c → ck/mycp\n[运行] ck/mycp test_cp_src.txt test_cp_dest.txt\n---\ncopy file finshed!\n---\n[退出码: 0]\nroot@kyriechao:~/work/linux/test# cmp -s test_cp_src.txt test_cp_dest.txt && echo \"ok\" ||echo \"fail\"\nok\nroot@kyriechao:~/work/linux/test# ck mycp.c -- AAA.txt test_cp_dest.txt\n[编译] mycp.c → ck/mycp\n[运行] ck/mycp AAA.txt test_cp_dest.txt\n---\nopen src: No such file or directory\n---\n[退出码: 255]\nroot@kyriechao:~/work/linux/test# ck mycp.c -- test_cp_src.txt test_cp_dest.txt\n[编译] mycp.c → ck/mycp\n[运行] ck/mycp test_cp_src.txt test_cp_dest.txt\n---\ndest test_cp_dest.txt already exists\n---\n[退出码: 255]\nroot@kyriechao:~/work/linux/test# touch AAA.txt\nroot@kyriechao:~/work/linux/test# ck mycp.c -- AAA.txt a.txt\n[编译] mycp.c → ck/mycp\n[运行] ck/mycp AAA.txt a.txt\n---\ncopy file finshed!\n---\n[退出码: 0]\nroot@kyriechao:~/work/linux/test# cmp -s AAA.txt a.txt && echo \"ok\" || echo \"fail\"\nok\nroot@kyriechao:~/work/linux/test# ls -l | grep AAA.txt\n-rw-r--r-- 1 root root     0 Oct 26 10:24 AAA.txt\nroot@kyriechao:~/work/linux/test# ls -l | grep a.txt\n-rw-r--r-- 1 root root     0 Oct 26 10:24 a.txt\nroot@kyriechao:~/work/linux/test# \n```\n超过 (1024*8)\n```bash\nroot@kyriechao:~/work/linux/test# dd if=/dev/urandom of=big.bin bs=1M count=100\n100+0 records in\n100+0 records out\n104857600 bytes (105 MB, 100 MiB) copied, 0.436657 s, 240 MB/s\nroot@kyriechao:~/work/linux/test# clear\nroot@kyriechao:~/work/linux/test# ck mycp.c -- big.bin big_copy.bin\n[编译] mycp.c → ck/mycp\n[运行] ck/mycp big.bin big_copy.bin\n---\ncopy file finshed!\n---\n[退出码: 0]\nroot@kyriechao:~/work/linux/test# cmp -s big.bin big_copy.bin && echo \"ok\" || echo \"fail\"ok\nroot@kyriechao:~/work/linux/test# \n\n```\n\n达到什么程度才算完成？\n\n你要能解释 `read()` 的三种返回情况：`-1` 表示出错、`0` 表示到达 EOF、正数表示实际读取的字节数。\n\n还有一种情况 `系统调用执行到一半收到信号` 就中断了 这里就是加了个重试机制。\n\n```c\n    ssize_t r = 0;\n    do {\n        r = read(src, buf, BUFF_SIZE);\n    } while (r < 0 && errno == EINTR);\n    if (r < 0) {\n        perror(\"read src\");\n        assess(dest, src);\n        exit(-1);\n    }\n    if (r == 0) break;\n```\n\n同时必须理解：`write()` 不保证一次写完所有请求的数据，因此要考虑短写；不能把一次 `read()` 当作读取整个文件。缓冲区不需要随着文件大小增长，循环分块处理即可。\n\n```c\n    ssize_t total = 0;\n    while (total < r) {\n        ssize_t w;\n        do {\n            w = write(dest, buf + total, r - total);\n        } while (w < 0 && errno == EINTR);\n        if (w < 0) {\n            perror(\"write dest\");\n            assess(dest, src);\n            exit(-1);\n        }\n        if (w == 0) {\n            fprintf(stderr, \"write returned 0\\n\");\n            assess(dest, src);\n            exit(-1);\n        }\n        total += w;\n    }\n```\n时间充足后，再补充 `lseek()` 和 `stat()`：写一个小程序，输出文件大小，并通过 `lseek()` 查看文件偏移。\n\nstat小程序\n\n```c\n#include <stdio.h>\n#include <stdlib.h>\n#include <sys/stat.h>\n#include <time.h>\n\nstatic void print_mode(mode_t mode) {\n    char type;\n    if (S_ISREG(mode)) type = '-';\n    else if (S_ISDIR(mode)) type = 'd';\n    else if (S_ISLNK(mode)) type = 'l';\n    else if (S_ISCHR(mode)) type = 'c';\n    else if (S_ISBLK(mode)) type = 'b';\n    else if (S_ISFIFO(mode)) type = 'p';\n    else if (S_ISSOCK(mode)) type = 's';\n    else type = '?';\n\n    char perm[10] = {0};\n    perm[0] = (mode & S_IRUSR) ? 'r' : '-';\n    perm[1] = (mode & S_IWUSR) ? 'w' : '-';\n    perm[2] = (mode & S_IXUSR) ? 'x' : '-';\n    perm[3] = (mode & S_IRGRP) ? 'r' : '-';\n    perm[4] = (mode & S_IWGRP) ? 'w' : '-';\n    perm[5] = (mode & S_IXGRP) ? 'x' : '-';\n    perm[6] = (mode & S_IROTH) ? 'r' : '-';\n    perm[7] = (mode & S_IWOTH) ? 'w' : '-';\n    perm[8] = (mode & S_IXOTH) ? 'x' : '-';\n    perm[9] = '\\0';\n\n    printf(\"st_mode         : 0%o (%c%s)\\n\", mode & 07777, type, perm);\n}\n\nstatic void print_time(const char *name, struct timespec ts) {\n    char buf[64] = {0};\n    struct tm tm;\n    localtime_r(&ts.tv_sec, &tm);\n    strftime(buf, sizeof(buf), \"%Y-%m-%d %H:%M:%S\", &tm);\n    printf(\"%-16s: %s.%09ld\\n\", name, buf, ts.tv_nsec);\n}\n\nint main(int argc, char **argv) {\n    if (argc != 2) {\n        fprintf(stderr, \"Usage: %s <path>\\n\", argv[0]);\n        return 1;\n    }\n\n    struct stat st;\n    if (stat(argv[1], &st) < 0) {\n        perror(\"stat\");\n        return 1;\n    }\n\n    printf(\"== stat: %s ==\\n\", argv[1]);\n    printf(\"st_dev          : %lu\\n\", (unsigned long)st.st_dev);\n    printf(\"st_ino          : %lu\\n\", (unsigned long)st.st_ino);\n    print_mode(st.st_mode);\n    printf(\"st_nlink        : %lu\\n\", (unsigned long)st.st_nlink);\n    printf(\"st_uid          : %u\\n\", st.st_uid);\n    printf(\"st_gid          : %u\\n\", st.st_gid);\n    printf(\"st_rdev         : %lu\\n\", (unsigned long)st.st_rdev);\n    printf(\"st_size         : %ld\\n\", (long)st.st_size);\n    printf(\"st_blksize      : %ld\\n\", (long)st.st_blksize);\n    printf(\"st_blocks       : %ld\\n\", (long)st.st_blocks);\n    print_time(\"st_atim\", st.st_atim);\n    print_time(\"st_mtim\", st.st_mtim);\n    print_time(\"st_ctim\", st.st_ctim);\n\n    printf(\"\\n== 判断宏 ==\\n\");\n    printf(\"S_ISREG         : %d\\n\", S_ISREG(st.st_mode));\n    printf(\"S_ISDIR         : %d\\n\", S_ISDIR(st.st_mode));\n    printf(\"S_ISLNK         : %d\\n\", S_ISLNK(st.st_mode));\n    printf(\"S_ISCHR         : %d\\n\", S_ISCHR(st.st_mode));\n    printf(\"S_ISBLK         : %d\\n\", S_ISBLK(st.st_mode));\n    printf(\"S_ISFIFO        : %d\\n\", S_ISFIFO(st.st_mode));\n    printf(\"S_ISSOCK        : %d\\n\", S_ISSOCK(st.st_mode));\n\n    printf(\"\\n== 权限位逐位 ==\\n\");\n    printf(\"S_IRUSR         : %d\\n\", !!(st.st_mode & S_IRUSR));\n    printf(\"S_IWUSR         : %d\\n\", !!(st.st_mode & S_IWUSR));\n    printf(\"S_IXUSR         : %d\\n\", !!(st.st_mode & S_IXUSR));\n    printf(\"S_IRGRP         : %d\\n\", !!(st.st_mode & S_IRGRP));\n    printf(\"S_IWGRP         : %d\\n\", !!(st.st_mode & S_IWGRP));\n    printf(\"S_IXGRP         : %d\\n\", !!(st.st_mode & S_IXGRP));\n    printf(\"S_IROTH         : %d\\n\", !!(st.st_mode & S_IROTH));\n    printf(\"S_IWOTH         : %d\\n\", !!(st.st_mode & S_IWOTH));\n    printf(\"S_IXOTH         : %d\\n\", !!(st.st_mode & S_IXOTH));\n\n    return 0;\n}\n```\n\n\n```bash\nroot@kyriechao:~/work/linux/test# ck file_info.c -- mycp.c\n[编译] file_info.c → ck/file_info\n[运行] ck/file_info mycp.c\n---\n== stat: mycp.c ==\nst_dev          : 64771\nst_ino          : 661239\nst_mode         : 0644 (-rw-r--r--)\nst_nlink        : 1\nst_uid          : 0\nst_gid          : 0\nst_rdev         : 0\nst_size         : 1828\nst_blksize      : 4096\nst_blocks       : 8\nst_atim         : 2026-10-26 10:15:29.321279549\nst_mtim         : 2026-10-26 10:15:29.311278520\nst_ctim         : 2026-10-26 10:15:29.314278828\n\n== 判断宏 ==\nS_ISREG         : 1\nS_ISDIR         : 0\nS_ISLNK         : 0\nS_ISCHR         : 0\nS_ISBLK         : 0\nS_ISFIFO        : 0\nS_ISSOCK        : 0\n\n== 权限位逐位 ==\nS_IRUSR         : 1\nS_IWUSR         : 1\nS_IXUSR         : 0\nS_IRGRP         : 1\nS_IWGRP         : 0\nS_IXGRP         : 0\nS_IROTH         : 1\nS_IWOTH         : 0\nS_IXOTH         : 0\n---\n[退出码: 0]\n```\n\nlseek\n\n```c\n#include <stdio.h>\n#include <stdlib.h>\n#include <unistd.h>\n#include <fcntl.h>\n\nint main(int argc, char **argv) {\n    if (argc != 2) {\n        fprintf(stderr, \"Usage: %s <path>\\n\", argv[0]);\n        return 1;\n    }\n    int fd = open(argv[1], O_RDONLY);\n    if (fd < 0) {\n        perror(\"open file\");\n        exit(-1);\n    }\n    char buf[64] = {0};\n    printf(\"初始偏移: %ld\\n\", (long)lseek(fd, 0, SEEK_CUR));\n    int n = read(fd, buf, 10);\n    if(n<0) {\n        close(fd);\n        exit(-1);\n    }\n    buf[n] = '\\0';\n    printf(\"read: %s\\n\", buf);\n    printf(\"读10字节后: %ld\\n\", (long)lseek(fd, 0, SEEK_CUR));\n    printf(\"文件大小: %ld\\n\", (long)lseek(fd, 0, SEEK_END));\n    lseek(fd, 0, SEEK_SET);\n    printf(\"回到头: %ld\\n\", (long)lseek(fd, 0, SEEK_CUR));\n    close(fd);\n    return 0;\n}\n\n```\n\n\n```bash\nroot@kyriechao:~/work/linux/test# ck lseek_demo.c -- lseek_demo.c\n[编译] lseek_demo.c → ck/lseek_demo\n[运行] ck/lseek_demo lseek_demo.c\n---\n初始偏移: 0\nread: #include <\n读10字节后: 10\n文件大小: 731\n回到头: 0\n---\n[退出码: 0]\n```\n\n### 复盘\n\n#### 今天搞懂的\n\n也不算今天搞懂的、懂的是更加细节的地方。\n\n例如 read、write的可能被系统中断知道了`EINTR`。\n\n对熟悉度我更了解了`write`的循环写入的。\n\n#### 踩过的坑\n\n以前就知道套公式、所以对total的位置不清楚写错了。\n\nwrite的也是忘记从哪里开始写\n\n<details>\n<summmary>DeepSeek所说的</summmary>\n\n#### 今天搞懂的\n- `read()` 三种返回值：`-1` 出错、`0` EOF、正数实际读到的字节数；循环里三个分支都要有。\n- `write()` 不保证一次写完，必须用 `total` 累加、从 `buf + total` 偏移续写，不能写一次就当完事。\n- 系统调用可能被信号打断返回 `EINTR`，这不是真错误，`read`/`write` 要 `do...while` 重试；`close` 遇到 `EINTR` 不重试。\n- 复制是“循环读一块、写一块”，缓冲区不随文件变大，100MB 文件也是 8KB 一块块过的。\n- `lseek` 管的是 fd 的**偏移量**（动态游标），`stat` 看的是文件**元数据**（静态属性），两回事。\n- `st_mode` 能拆出文件类型和权限；`st_dev + st_ino` 才能唯一确定一个文件。\n\n#### 踩过的坑\n- `total` 定义在循环外没重置，第二次读直接跳过写入。\n- `write` 没从 `buf + total` 开始写，短写时会重复写旧数据。\n- `perror` 文案写错（`read` 失败报成 `write`）。\n- 宏没加括号、`finshed` 拼错，小问题但容易留。\n\n### 待补\n- 目标已存在用 `O_CREAT | O_EXCL` 做到原子失败，已实现；但 `O_TRUNC` 覆盖模式下的“复制失败毁掉原目标”还没处理，后面了解临时文件 + `rename`。\n- `fsync` 保证落盘、`fchmod`/`futimens` 保留元数据，知道有这些，暂不实现。\n- 性能上可以了解 `copy_file_range`、`reflink`，现在 8KB 循环够用。\n\n</details>",
            "url": "https://kyriechao.github.io/posts/2026/10/26-diary",
            "title": "2026-10-26 周一、模拟面试开始",
            "summary": "有点排斥这个模拟面试 都特别急的执行 昨天爬完长城腿酸啊",
            "date_modified": "2026-10-26T01:04:25.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/24-weekly-plan",
            "content_html": "\n## 本周重点\n\n\n你说得对。之前的计划只有知识点，没有明确的练习题、代码产出和验收标准，而且没有给当天的新课留出位置。\n\n下周应该采用“双线推进”：当天的新课正常学习，旧知识每天安排一个具体的编程任务。 不要求六天内精通 Day1–Day10，而是让每一天都有可以验证的结果。\n\n# 10 月 26 日—31 日计划\n\n## 先确定每天的时间分配\n\n考虑到你培训时间较长，我建议按以下结构安排：\n\n第一条线：当天的新知识\n\n正常跟进培训课程。课后用 10–15 分钟记录新概念、关键 API、尚未解决的问题；有代码示例就自己复现一个最小版本。\n\n第二条线：旧知识实战\n\n工作日安排约 30–45 分钟，周六可适当延长。先独立完成题目，再查笔记、编译、运行和排错。\n\n以下是旧知识实战的具体安排。当天新课的主题以老师实际讲授内容为准，不预先虚构课程进度。如果当天有必须完成的培训作业，优先完成作业，再适当缩减旧知识练习。\n\n## 10 月 26 日，周一：Day 1 文件操作\n\n核心任务：独立实现文件复制程序\n\n具体练习\n\n编写 `mycp.c`，实现以下命令：\n\n```bash\n./mycp source.txt destination.txt\n```\n\n要求：\n\n1. 使用 `open()` 打开源文件、创建目标文件。\n2. 使用固定大小的缓冲区，例如 `char buf[1024]`。\n3. 使用循环调用 `read()`，直到文件读取结束。\n4. 对每次读取到的数据使用 `write()` 写入目标文件。\n5. 正确处理文件描述符关闭和错误返回。\n\n必须测试的情况：\n\n- 源文件为空。\n- 源文件小于 1024 字节。\n- 源文件大于 1024 字节。\n- 源文件不存在。\n- 目标文件已存在。\n\n达到什么程度才算完成？\n\n你要能解释 `read()` 的三种返回情况：`-1` 表示出错、`0` 表示到达 EOF、正数表示实际读取的字节数。\n\n同时必须理解：`write()` 不保证一次写完所有请求的数据，因此要考虑短写；不能把一次 `read()` 当作读取整个文件。缓冲区不需要随着文件大小增长，循环分块处理即可。\n\n时间充足后，再补充 `lseek()` 和 `stat()`：写一个小程序，输出文件大小，并通过 `lseek()` 查看文件偏移。\n\n## 10 月 27 日，周二：Day 2 库\n\n核心任务：制作并使用静态库和共享库\n\n建立一个简单项目：\n\n```\nlibrary_demo/\n├── calc.h\n├── calc.c\n└── main.c\n```\n\n`calc.c` 实现 `add()` 和 `sub()`，`main.c` 调用它们。\n\n必须完成：\n\n1. 编译出 `libcalc.a`。\n2. 使用静态库链接生成可执行文件，并运行。\n3. 编译出 `libcalc.so`。\n4. 使用共享库链接生成另一个可执行文件，并运行。\n5. 将共享库放到单独的目录，使用 `LD_LIBRARY_PATH` 测试运行时搜索。\n\n验收标准\n\n你能不照抄命令，独立完成下面的流程：\n\n```bash\ncalc.c → calc.o → libcalc.a → 静态链接程序\ncalc.c → calc.o → libcalc.so → 动态链接程序\n```\n\n并能解释 `-I`、`-L`、`-l`、`-fPIC`、`-shared` 分别解决什么问题，以及为什么共享库链接成功后仍可能在运行时找不到。\n\n这一天不要求掌握 `dlopen()` 插件机制，只需能说明它的用途。\n\n## 10 月 28 日，周三：Day 3 进程\n\n核心任务：实现 fork、waitpid、exec 的组合程序\n\n编写 `process_demo.c`：\n\n1. 父进程使用 `fork()` 创建子进程。\n2. 子进程打印自己的 PID 和 PPID，然后以状态码 `7` 退出。\n3. 父进程使用 `waitpid()` 等待子进程。\n4. 父进程使用 `WIFEXITED()` 和 `WEXITSTATUS()` 获取并打印子进程退出状态。\n5. 再编写一个独立小例子，让子进程用 `execl()` 执行 `/bin/ls`，观察程序替换后的执行过程。\n\n验收标准\n\n不看笔记，你需要能解释：\n\n- `fork()` 为什么在父子进程中返回不同的值。\n- `fork()` 后父子进程分别从哪里继续执行。\n- `exec` 成功后，原程序的代码为什么不再继续执行。\n- `waitpid()` 的作用，以及退出状态 `7` 如何被取出。\n- 为什么子进程退出后，父进程不及时回收可能产生僵尸进程。\n\n`vfork()`、进程组、会话和守护进程这一天只做概念复述，不要求同时实现。\n\n## 10 月 29 日，周四：Day 4–5 进程间通信\n\n核心任务：真正跑通一次父子进程通信\n\n第一项：匿名管道程序\n\n使用 `pipe()` 和 `fork()`，实现：\n\n```\n父进程\n  │ write(\"hello child\")\n  ▼\n匿名管道\n  │ read()\n  ▼\n子进程\n  └── 打印收到的内容\n```\n\n要求正确关闭父子进程不需要的管道端，并验证关闭所有写端后，读端最终会读到 EOF。\n\n第二项：信号小练习\n\n编写一个简单的 `SIGALRM` 示例，观察定时信号的触发。理解信号的默认处理、自定义处理以及 `pause()` 的用途。信号处理函数中不要直接进行不安全的复杂操作，例如直接调用普通的 `printf()`。\n\n第三项：IPC 口述验收\n\n不强求一天之内再实现完整的共享内存和消息队列程序，但以下问题必须答得出来：\n\n- 管道、共享内存、消息队列、信号分别适合什么情形？\n- 为什么共享内存通常还需要同步机制？\n- `shmdt()` 和 `shmctl()` 的作用有什么区别？\n- 孤儿进程和僵尸进程的区别是什么？\n\n达到什么程度？\n\n管道程序必须能够编译运行；其余知识能够结合调用流程解释清楚。如果共享内存或消息队列的调用顺序还不熟悉，就记为下一轮待练项目，不用假装已经掌握。\n\n## 10 月 30 日，周五：Day 6–8 线程与同步\n\n核心任务：复现竞态条件，并用锁修复\n\n这一天不实现完整的线程池。先把共享数据保护做扎实，再处理高级同步。\n\n第一项：竞态条件\n\n创建多个线程，让它们共同对一个全局计数器执行递增操作，例如 4 个线程各自递增 100,000 次。\n\n先不加锁，运行观察最终结果；再用 `pthread_mutex_t` 保护递增操作，重新运行。\n\n验收要求：理解为什么 `counter++` 不能简单视为一个不可分割的原子操作，以及为什么加锁后结果可以符合预期。\n\n第二项：条件变量\n\n写出一个最小的生产者—消费者示例，或先补齐其关键代码：\n\n- 共享数据由互斥锁保护。\n- 条件不满足时使用 `pthread_cond_wait()`。\n- 等待条件用 `while` 检查，而不是只用 `if`。\n- 条件满足后由生产者或消费者发出合适的通知。\n\n第三项：概念测试\n\n能区分互斥锁、信号量和条件变量的主要用途；能说出死锁的四个必要条件；能够说明 Joinable 和 Detached 线程的资源回收方式。\n\n线程池这一天只要求画出“任务队列—工作线程—同步机制”的结构，不实现完整线程池。\n\n## 10 月 31 日，周六：Day 9–10 网络通信\n\n核心任务：独立实现 TCP Echo Client/Server\n\n这也是下周最值得重视的一天，因为它和你正在学习的网络通信课程、后续工业设备通信项目直接相关。\n\n第一项：服务端\n\n编写一个服务端程序，实现：\n\n```text\nsocket()\n   ↓\nbind()\n   ↓\nlisten()\n   ↓\naccept()\n   ↓\nrecv() / send()\n   ↓\nclose(conn_fd)\n```\n\n服务端先只支持一次处理一个客户端，不要求多线程或高并发。\n\n第二项：客户端\n\n编写对应客户端，使用 `socket()` 和 `connect()` 建立连接，然后发送文本、接收回显并正确关闭套接字。\n\n第三项：故障测试\n\n至少进行以下测试：\n\n- 客户端发送正常文本，服务端正确回显。\n- 客户端退出，服务端识别 `recv()` 返回 `0`。\n- 客户端尝试连接错误的端口，观察并分析错误。\n- 修改端口配置，练习 `htons()` 和 `setsockopt()` 的使用场景。\n\n验收标准\n\n你要能解释 `listen_fd` 和 `conn_fd` 的区别，说明 TCP 是字节流而非消息协议，以及为什么 `send()` 和 `recv()` 的一次调用不一定对应一次完整的应用层消息。\n\n第一版不要求处理复杂协议分帧，也不要求实现高并发。先把连接建立、数据收发、连接关闭和基本错误检查写正确。\n\n## 最后，怎么判断这六天有没有效果？\n\n别用“看过了”“好像明白了”作为完成标准。每天给自己记录三个状态：\n\n| 状态       | 判断条件                 |\n| -------- | -------------------- |\n| A：独立完成   | 不看答案，可以写出主要代码并编译运行   |\n| B：借助资料完成 | 查看 API 文档或笔记后能够修正并跑通 |\n| C：尚未掌握   | 需要参考完整答案，或无法解释关键执行过程 |\n\n每个任务都可以标记为 A、B 或 C。标记 C 不意味着当天失败，而是明确告诉你下一轮练什么。\n\n我的判断是：下周不应该以“复习完十天”作为目标，而应该以至少完成四个可以运行的程序为底线：文件复制、库调用、进程控制、TCP 通信；并额外完成一次线程竞态与锁的验证。 IPC 其余机制和高级同步则先达到能解释、能定位、知道如何调用的程度。\n\n这样既没有把你每天的新课挤掉，也能让旧知识真正进入编程实践。",
            "url": "https://kyriechao.github.io/posts/2026/10/24-weekly-plan",
            "title": "2026-10-24 周规划",
            "summary": "今天做周规划、周六晚上就休息了",
            "date_modified": "2026-10-24T09:07:05.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/24-diary",
            "content_html": "\n\n## 日常\n\n今天上午状态挺好的、ChatGPT差点把我气疯了。\n\n我原本就是demo级别的练习、到后面用健硕代码的规格挑刺儿。\n\n今天还想着规划下周的、真的是有心无力了。\n\n今天已经对POSIX的 `open`、`read`、`write`、`lseek` 等等 熟悉了。\n\n\n\n<details>\n\n<summary>ChatGPT 今日规划</summary>\n今天统一采用一个原则：先独立尝试，再根据实际卡点查资料，最后用运行结果验收。 不以代码写得快不快作为唯一标准。\n\n# 一、统一测试规则\n\n每个任务建议限时 30 分钟独立尝试，之后再花 15–25 分钟解决问题。三个任务全部完成后，再根据情况复测。\n\n测试时遵循以下规则：\n\n1. 开始前关闭课程笔记，不复制以前写过的代码。\n2. 可以使用编辑器、编译器和终端；独立阶段不查旧代码、不问 AI、不搜索实现方案。\n3. 独立阶段结束后，记录当前状态，再决定是否查阅 `man` 手册、课程笔记或其他资料。\n4. 每次程序运行都要观察实际结果，不能只以“编译成功”作为完成标准。\n\n## 三个等级如何判定\n\n| 等级        | 判定标准                                      |\n| --------- | ----------------------------------------- |\n| A：独立完成    | 不查实现资料，独立编写、编译、运行成功；能解释关键系统调用和程序行为。       |\n| B：查阅资料后完成 | 独立阶段未完成或存在错误；通过查阅手册、笔记等解决问题，随后能解释并验证修复结果。 |\n| C：未能完成    | 查阅资料后仍无法完成核心功能，或程序现象与预期不符且无法解释。           |\n\n这里有个重要区别：查过 `man 2 read` 的参数不代表你没有独立能力；直接复制现成程序却说不清原理，也不算真正掌握。\n\n# 二、任务一：文件 I/O\n\n## 独立编写文件复制程序\n\n建议用时：30 分钟独立尝试 + 15–25 分钟纠错\n\n### 测试要求\n\n编写一个 C 程序，将源文件内容复制到目标文件。\n\n例如：\n\n```\n./copy source.txt target.txt\n```\n\n程序至少需要做到：\n\n- 使用 `open()` 打开源文件。\n- 使用 `open()` 创建或打开目标文件，并设置合适的权限与标志。\n- 使用 `read()` 读取内容，使用 `write()` 写入目标文件。\n- 正确处理文件结束、系统调用失败和文件描述符关闭。\n\n### 验收标准\n\n基本复制成功\n\n源文件与目标文件内容一致。\n\n空文件测试\n\n源文件为空时，程序正常结束。\n\n较大文件测试\n\n文件内容较多时仍能完整复制。\n\n错误处理\n\n源文件不存在时，程序给出错误信息并正常退出。\n\n解释关键逻辑\n\n能说明 read() 返回 0、返回 -1，以及 write() 未写完全部数据时分别应如何处理。\n\n已验证 0/5 项。这里的勾选仅用于记录测试结果。\n\n重点观察： 你是否考虑了 `write()` 可能只写入部分字节，而不是默认一次调用就能写完全部内容。这是文件 I/O 中值得特别检查的边界情况。\n\n# 三、任务二：进程控制\n\n## 使用 fork() 与 waitpid()\n\n建议用时：30 分钟独立尝试 + 15–25 分钟纠错\n\n### 测试要求\n\n编写一个程序，使用 `fork()` 创建子进程，由父进程使用 `waitpid()` 等待并回收子进程。\n\n要求能够区分父子进程，并观察它们的执行行为。\n\n### 验收标准\n\n正确创建进程\n\nfork() 成功后，父进程与子进程能够执行各自的分支。\n\n理解返回值\n\n能解释 fork() 在父进程和子进程中分别返回什么。\n\n等待并回收\n\n父进程调用 waitpid()，且能够确认等待结果。\n\n检查退出状态\n\n能获取并解释子进程的退出状态，例如正常退出与异常终止的区别。\n\n解释执行顺序\n\n知道父子进程的输出顺序不一定固定，也知道 waitpid() 能保证什么、不能保证什么。\n\n已验证 0/5 项。\n\n重点观察： 你是否真正理解 `fork()` 之后执行流分成两条，以及 `waitpid()` 不只是“等一等”，还承担回收子进程资源的作用。\n\n# 四、任务三：管道通信\n\n## 使用 pipe() 实现父子进程通信\n\n建议用时：30 分钟独立尝试 + 15–25 分钟纠错\n\n### 测试要求\n\n编写一个 C 程序：\n\n1. 使用 `pipe()` 创建匿名管道。\n2. 使用 `fork()` 创建子进程。\n3. 让一方写入一段消息，另一方读取并打印消息。\n4. 正确关闭双方不需要的管道端。\n\n今天先做单向通信，不需要扩展成双向通信或多进程通信。\n\n### 验收标准\n\n消息传递成功\n\n接收方打印的消息与发送方写入的内容一致。\n\n理解管道两端\n\n能够区分 pipefd[0] 的读端与 pipefd[1] 的写端。\n\n正确关闭文件描述符\n\n读方关闭不需要的写端，写方关闭不需要的读端。\n\n程序能够正常结束\n\n不会因为某一端仍然打开而导致读取端一直等待 EOF。\n\n解释通信机制\n\n能说明为什么管道需要在 fork() 前创建，以及父子进程如何继承管道文件描述符。\n\n已验证 0/5 项。\n\n重点观察： 管道通信最容易出现的问题不是 `pipe()` 调用本身，而是文件描述符的继承和关闭。某个进程仍然持有写端时，读端可能一直等不到 EOF。\n\n# 五、今天最终要留下什么结果？\n\n三个任务都做完后，不需要写长篇学习总结。每项只记录下面这些内容即可：\n\n| 记录项  | 你需要回答的问题                          |\n| ---- | --------------------------------- |\n| 最终等级 | A 独立完成、B 查资料后完成，还是 C 未能完成？        |\n| 实际结果 | 程序是否编译、运行成功？测试了什么？                |\n| 卡点   | 最先卡在哪里？忘记 API、缺少实现思路，还是对运行机制理解不清？ |\n| 后续动作 | 是否需要隔一段时间再独立写一次？                  |\n\n还有一个限制：不要因为某项拿到 B 或 C，就立刻扩展出十个新任务。 先完成三个抽查，再看结果决定下周的重点。\n\n如果三个任务都能独立完成且解释清楚，下一步就不必继续反复练习这些基础例子，可以进入更接近真实项目的综合练习。如果某项需要查资料，也不必推翻之前的学习成果，只针对暴露出来的具体问题复练。\n\n今天的目标不是把 Linux 系统编程全部学完，而是获得三个可靠的能力判断。\n</details>",
            "url": "https://kyriechao.github.io/posts/2026/10/24-diary",
            "title": "2026-10-24 周六休息 被ChatGPT气疯",
            "summary": "上午状态好好的ChatGPT一顿挑刺 心情都被搞坏了原本今天做周规划+复习Linux高编的",
            "date_modified": "2026-10-24T01:58:47.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/23-diary",
            "content_html": "\n\n## 日常\n\n### 简历作业\n\n终于通过了！但是也不知道怎么过的、可能是写太多次不耐烦让过了、也有可能是勉强合格、也有可能写的不错 但是最后一种可能概率低。\n\n![简历过了](/images/2026/10/23/1.png)\n\n今晚也要写项目介绍、自我介绍等等\n\n\n<details>\n<summary>DeepSeek 生成的</summary>\n\n### 第一部分：自我介绍（直接背，大约 1.5 分钟）\n\n> 面试官您好，我叫朝格吐，来自内蒙古农业大学计算机科学与技术专业。我的求职意向是嵌入式软件工程师和 Linux 应用开发。\n> \n> 在校期间，我系统掌握了 C/C++ 开发、数据结构以及 Linux 操作系统底层原理。我的技术栈涵盖了**Linux 应用开发、网络编程、内核驱动，以及 STM32 单片机与 FreeRTOS 系统**。我具备从底层外设驱动编写、RTOS 任务调度，到上层 Linux 并发服务器开发，以及 Qt 上位机软件编写的全链路开发能力。\n> \n> 在项目方面，我曾在成都万创科技实习，参与了 G335 工业边缘计算网关的开发，负责南向协议采集和北向 MQTT 上云；在校期间，我独立完成了 ARM-Linux 平台的驱动封装，还参与过车载 CAN 总线终端以及多自由度稳控云台的项目。此外，我具备软硬协同的排障能力，能熟练使用示波器、逻辑分析仪和 Wireshark 抓包来定位问题。\n> \n> 我非常热爱底层开发，希望能在贵公司得到一个实习/工作的机会，谢谢！\n\n---\n\n### 第二部分：项目介绍（以 G335 为例，流畅口述版）\n\n*（老师说了，项目介绍不用一字不差背，要像讲故事一样讲出来）*\n\n> 我介绍的项目是万创的 G335 工业边缘计算网关。\n> \n> **【背景】**：这个产品主要是用在工业现场的，因为现场有很多 PLC 和传感器，网络环境也比较复杂，所以需要一个网关把数据汇总，然后传到云平台上。\n> \n> **【职责与难点】**：在这个项目中，我主要负责南向的数据采集和北向的 MQTT 上云。难点在于怎么保证在高频数据流下系统不卡顿，以及弱网断线后数据不丢失。\n> \n> **【行动】**：我使用 Linux Socket 编写了 Modbus TCP 客户端去轮询 PLC，引入了 epoll 多路复用来管理多设备的并发连接；为了防止断网丢数据，我配合团队设计了 RingBuffer 环形缓冲区，等网络恢复后自动补发；数据上传前，我用 cJSON 进行了序列化封装。\n> \n> **【结果】**：最后这个网关能稳定支持多路设备的并发接入，即使网络波动也能稳定重连。同时我也负责用 Wireshark 抓包和 GDB 调试，解决了多起报文粘包和丢包的问题。\n\n---\n\n### 第三部分：项目押题（核心！按 STAR 法则拆解）\n\n老师让你按“背景、功能、架构、模块实现、业务流程、数据结构、遇到问题怎么调试”来拆。**以下是 G335 的押题范例：**\n\n*   **Q1：为什么用 epoll 而不用 select？**\n    *   **A（Action）**：G335 这种网关可能要同时连接几十上百个设备。select 每次调用都要把所有 fd 从用户态拷贝到内核态，而且返回后还要遍历所有 fd，效率很低。epoll 是基于事件驱动的，没有 fd 数量限制，而且不需要每次都拷贝，它在海量并发且只有少量活跃连接时，性能极高，所以更适合工业网关。\n*   **Q2：遇到网络波动导致数据丢失（或粘包），你是如何调试和解决的？**\n    *   **A（Action + Result）**：首先，我在设备端打日志，确认是物理网络断了还是应用层没发出去。然后，我用 Wireshark 抓包，发现是报文没有加包头和包尾，导致接收端无法判断数据边界（粘包）。最后，我在协议里增加了帧头帧尾和长度字段，并配合 **RingBuffer 环形缓冲区**，断网时先存本地，网络恢复后自动重发，解决了丢包问题。\n*   **Q3：设备假死怎么处理？**\n    *   **A**：我在代码里加了心跳保活机制（Keep-Alive）和超时重试机制。如果发送报文后，规定时间（比如 3 秒）没收到回复，就自动重连，或者切换备用网络链路。\n\n*(你自己写其他项目时，也要准备好：CAN 总线遇到丢帧怎么查？云台 PID 振荡怎么调？)*\n\n---\n\n### 第四部分：技术押题（简历写了啥，面试就问啥）\n\n老师强调了，根据你简历里的技术栈，每项准备 1-3 个问题。给你列几个必考的核心题：\n\n**1. C/C++**\n*   **问**：你了解智能指针吗？解决什么问题的？\n*   **答**：了解。`unique_ptr` 独占所有权，`shared_ptr` 是共享所有权（引用计数）。它们主要用来防止忘记 `delete` 导致的内存泄漏，利用了 **RAII**（资源获取即初始化）的机制，出了作用域自动释放。\n\n**2. Linux 应用与网络**\n*   **问**：多线程怎么同步？死锁怎么产生的？\n*   **答**：用互斥锁、条件变量。死锁通常是因为两个线程互相持有对方需要的锁，并且不释放。解决方法是：按顺序加锁，或者使用 `std::lock_guard` 自动解锁。\n*   **问**：TCP 和 UDP 区别？\n*   **答**：TCP 面向连接、可靠、流式传输，适合文件传输和 Modbus；UDP 无连接、快、可能丢包，适合视频流或高频传感器数据。\n\n**3. STM32/RTOS/底层**\n*   **问**：DMA 是什么？怎么用？\n*   **答**：DMA 是直接内存访问，不占用 CPU。我通常用它配合串口或 SPI 接收数据，比如 CAN 总线高频收发时，配置 DMA 接收，处理完一批再触发中断，大大减轻了 CPU 负担。\n*   **问**：FreeRTOS 任务调度原理？任务怎么通信的？\n*   **答**：基于优先级的抢占式调度，高优先级任务就绪立马打断低优先级。任务之间通常用消息队列、信号量、互斥锁来通信。\n</details>\n\n### 力扣\n\n单词规律\n\n给定一种规律 pattern 和一个字符串 s ，判断 s 是否遵循相同的规律。\n\n这里的 遵循 指完全匹配，例如， pattern 里的每个字母和字符串 s 中的每个非空单词之间存在着双向连接的对应规律。具体来说：\n\npattern 中的每个字母都 恰好 映射到 s 中的一个唯一单词。\ns 中的每个唯一单词都 恰好 映射到 pattern 中的一个字母。\n没有两个字母映射到同一个单词，也没有两个单词映射到同一个字母。\n \n\n示例1:\n输入: pattern = \"abba\", s = \"dog cat cat dog\"\n输出: true\n\n示例 2:\n输入:pattern = \"abba\", s = \"dog cat cat fish\"\n输出: false\n\n示例 3:\n输入: pattern = \"aaaa\", s = \"dog cat cat dog\"\n输出: false\n```c\nbool wordPattern(char* pattern, char* s) {\n    int len = strlen(pattern);\n    int p_code[300];\n    int p_dict[26];\n    for (int i = 0; i < 26; i++)\n        p_dict[i] = -1;\n\n    for (int i = 0; i < len; i++) {\n        int idx = pattern[i] - 'a';\n        if (p_dict[idx] == -1) {\n            p_dict[idx] = i;\n            p_code[i] = i;\n        } else {\n            p_code[i] = p_dict[idx];\n        }\n    }\n\n    char* words[300];\n    int w_cnt = 0;\n    char* token = strtok(s, \" \");\n    while (token != NULL) {\n        words[w_cnt++] = token;\n        token = strtok(NULL, \" \");\n    }\n    if (w_cnt != len)\n        return false;\n    for (int i = 0; i < w_cnt; i++) {\n        int current_code = -1;\n        for (int j = 0; j < i; j++) {\n            if (strcmp(words[j], words[i]) == 0) {\n                current_code = j;\n                break;\n            }\n        }\n\n        if (current_code == -1) {\n            current_code = i;\n        }\n        if (current_code != p_code[i]) {\n            return false;\n        }\n    }\n    return true;\n}\n```",
            "url": "https://kyriechao.github.io/posts/2026/10/23-diary",
            "title": "2026-10-23 简历作业过了",
            "summary": "也不知道怎么过的 可能是写太多次不耐烦让过了 也有可能是勉强合格 也有可能写的不错 但是最后一种可能概率低",
            "date_modified": "2026-10-23T00:54:48.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/22-diary",
            "content_html": "\n## 日常\n\n### 早晨\n\n早晨出来的时候\n\n![早晨](/images/2026/10/22/2.jpg)\n\n这个街 走几次就不走喽\n\n![早晨](/images/2026/10/22/3.jpg)\n\n### 就业课相关\n\n今天似乎没做其他事情了、修改简历这件事真的很让我头痛。\n\n还没定稿 虽说勉强合格 但是我还会继续改改的 作业还加了 项目介绍、个人介绍等等好多。\n\n感觉做不想做的事情 挺缺很累。\n\n### 力扣\n\n#### 回文链表\n\n**题目描述**\n\n给你一个单链表的头节点 head ，请你判断该链表是否为回文链表。如果是，返回 true ；否则，返回 false。\n\n**我的实现**\n\n第一种方法 我第一个想到就是反转之后遍历就可以\n\n```c\nstruct ListNode* copyList(struct ListNode* head) {\n    if (head == NULL) return NULL;\n    struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));\n    struct ListNode* tail = dummy;\n    \n    struct ListNode* curr = head;\n    while (curr != NULL) {\n        struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));\n        newNode->val = curr->val;\n        newNode->next = NULL;\n        tail->next = newNode;\n        tail = newNode;\n        curr = curr->next;\n    }\n    struct ListNode* newHead = dummy->next;\n    free(dummy);\n    return newHead;\n}\n\nbool isPalindrome(struct ListNode* head) {\n    struct ListNode* p = copyList(head);\n    struct ListNode* curr = head;\n    struct ListNode* prev = NULL;\n    struct ListNode* next = NULL;\n    while (curr != NULL) {\n        next = curr->next;\n        curr->next = prev;\n        prev = curr;\n        curr = next;\n    }\n    while (p != NULL && prev != NULL) {\n        if (p->val == prev->val) {\n            p = p->next;\n            prev = prev->next;\n        } else {\n            return false;\n        }\n    }\n    if (p == NULL && prev == NULL) {\n        return true;\n    } else {\n        return false;\n    }\n}\n```\n\n**我的实现**\n\n第二种方法 就是跟求数字的回文数一样 取到一半 去对比 **经典的快慢指针**\n\n```c\nbool isPalindrome(struct ListNode* head) {\n    struct ListNode* slow = head;\n    struct ListNode* fast = head;\n    struct ListNode* p = head;\n\n    while (fast->next != NULL && fast->next->next != NULL) {\n        slow = slow->next;\n        fast = fast->next->next;\n    }\n    struct ListNode* curr = slow->next;\n    struct ListNode* prev = NULL;\n    struct ListNode* next = NULL;\n    while (curr != NULL) {\n        next = curr->next;\n        curr->next = prev;\n        prev = curr;\n        curr = next;\n    }\n    while (prev != NULL) {\n        if (prev->val == p->val) {\n            prev = prev->next;\n            p = p->next;\n        }else{\n            return false;\n        }\n    }\n    return true;\n}\n```\n\n#### 丑数 就是只包含质因数 2、3 和 5 的 正 整数。\n\n**题目描述**\n\n给你一个整数 n ，请你判断 n 是否为 丑数 。如果是，返回 true ；否则，返回 false 。\n\n**我的实现**\n\n```c\nbool isUgly(int n) {\n    int m =0;\n    while(1){\n        m = n;\n        if(n==1) break;\n        if(n%5==0) n/=5;\n        if(n%3==0) n/=3;\n        if(n%2==0) n/=2;\n        if(m==n) return false;\n    }\n    return true;\n}\n```\n\n#### 第一个错误的版本\n\n**题目描述**\n\n你是产品经理，目前正在带领一个团队开发新的产品。不幸的是，你的产品的最新版本没有通过质量检测。由于每个版本都是基于之前的版本开发的，所以错误的版本之后的所有版本都是错的。\n\n假设你有 n 个版本 [1, 2, ..., n]，你想找出导致之后所有版本出错的第一个错误的版本。\n\n你可以通过调用 bool isBadVersion(version) 接口来判断版本号 version 是否在单元测试中出错。实现一个函数来查找第一个错误的版本。你应该尽量减少对调用 API 的次数。\n\n**我的实现**\n\n第一个版本 竟然超时了 我看到 n=2000000000 我就懂了 \n\nint是4字节 $2^{31}$ -1 我也不知道我为什么这么喜欢递归 DeepSeek这么说的 给我爽到了\n\n```c\nint firstBadVersion(int n) {\n    if (isBadVersion(n)) {\n        return firstBadVersion(n - 1);\n    } else {\n        return n + 1;\n    }\n}\n```\n\n后续通过**DeepSeek**的解答 我用了 二分查找\n\n```c\nint firstBadVersion(int n) {\n    int left = 1;\n    int right = n;\n    while (left < right) {\n        int mid = left + (right - left) / 2;\n        if (isBadVersion(mid)) {\n            right = mid;\n        } else {\n            left = mid + 1;\n        }\n    }\n    return left;\n}\n```\n\n然后 我又用 递归实现了 二分查找 \n\n**我的实现**\n\n```c\nint binary_find(int left, int right) {\n    if (left >= right)\n        return left;\n    int mid = left + (right - left) / 2;\n    if (isBadVersion(mid))\n        return binary_find(left, mid);\n    else\n        return binary_find(mid + 1, right);\n}\n\nint firstBadVersion(int n) {\n    return binary_find(1, n);\n}\n```\n\n#### 递归的思考\n\n**我的疑问**：我很喜欢用递归，但总听说要小心递归，为什么？问了 DeepSeek（2026-10-22）。\n\n![DeepSeek回答](/images/2026/10/22/1.png)\n\n**我的总结**：\n\n- 适用：树、图的深搜、回溯这类结构本身就是递归的问题\n- 风险：栈空间占用大，层数太深会栈溢出；还可能重复计算（如斐波那契）\n- 对策：加记忆化、改成循环\n- 注意：C 标准不保证尾递归优化，上面的递归二分不能依赖编译器优化；DeepSeek 说的“99% 的老大都会要求改成while”没有依据，仅供参考\n\n<details>\n<summary>DeepSeek 的完整回答</summary>\n\n哈哈哈，作为一个程序员，喜欢递归简直太正常了！甚至可以说，**喜欢递归，说明你具备了用“上帝视角”去拆解问题的能力。**\n\n你知道吗？计算机科学里有一句名言：“**人理解迭代，神理解递归**。”\n\n让我来帮你分析一下，为什么你会不知不觉爱上递归，以及怎么驾驭这种“魔法”：\n\n### 🧠 为什么你会喜欢递归？\n\n1.  **符合人类的“偷懒”天性（分而治之）**\n    递归的本质就是：“遇到大问题，把它缩小成一个小一点的问题，直到小到我闭着眼睛都能解决。”\n    比如你之前写杨辉三角，你的潜台词是：“别让我手算每一行，你直接把上一行给我拿来，我加一下就完事了。”这种思维非常顺滑。\n2.  **代码极其优雅（装X利器）**\n    相比于写一堆 `while`、`if`、`i++`、`j++` 的循环，递归往往三四行代码就能搞定一切，读起来就像读诗一样。\n3.  **数学归纳法的直觉**\n    你在高中数学里学的“第一项成立，假设第 n 项成立推导第 n+1 项也成立”，其实和写递归一模一样。你天然具备这种逻辑推演能力。\n\n---\n\n### ⚠️ 但为什么大佬总是警告“小心递归”？\n\n既然递归这么爽，为什么在某些公司里，代码里看到递归，主管的血压就会飙升？\n\n1.  **爆栈危机（Stack Overflow）**\n    你之前的超时题还记得吗？递归一万次、一百万次，系统调用栈直接给你干碎。递归就像是“把活全包给下属去干”，一层一层往下包，最后底层没空间了，整个公司（程序）直接崩溃。\n2.  **重复计算（重叠子问题）**\n    递归很容易“犯傻”。比如算斐波那契数列 `fib(5)` 需要算 `fib(4)` 和 `fib(3)`；但算 `fib(4)` 时又要算一次 `fib(3)`。它总在反复做同样的无用功，时间直接呈指数爆炸。\n3.  **性能开销**\n    函数调用本身就是有成本的（压参数、存返回地址、跳转）。递归每次都要在这上面耗费一点点时间，积少成多，往往不如一个极简的 `while` 循环来得快。\n\n---\n\n### 🎯 什么时候该毫不犹豫地用递归？\n\n不用克制自己，遇到以下场景，直接上递归，你会发现它无敌好用：\n*   **数据结构是“树”**：二叉树、文件系统目录（你之前练习的那个 tree 项目）。\n*   **数据结构是“图”**：图的深度优先搜索（DFS）。\n*   **回溯算法**：走迷宫、下棋、八皇后（不停地试错，错了就退回上一步）。\n\n### 🛡️ 怎么改造“又爱又恨”的递归？\n\n1.  **加个备忘录（Memoization）**：如果你发现递归重复算了很多次（比如斐波那契），就拿个数组把算过的结果存起来。每次进来先查表，有表直接拿，这叫“记忆化搜索”，瞬间起飞。\n2.  **想想能不能改造成“循环”**：如果是一维数组遍历、二分查找这种线性的问题，试着强制自己用 `while` 写一遍。比如你刚刚的二分，虽然你用递归写得很爽，但在企业级开发中，99% 的老大都会要求你改成 `while(left < right)`。\n3.  **尾递归优化**：就像你二分查找那样，把递归放在函数的最后一句 `return binary_find(...)`，现代编译器有时能把它自动优化成循环，就不怕爆栈了。\n\n</details>\n\n#### 移动零\n\n**题目描述**\n\n给定一个数组 nums，编写一个函数将所有 0 移动到数组的末尾，同时保持非零元素的相对顺序。\n\n请注意 ，必须在不复制数组的情况下原地对数组进行操作。\n\n**我的实现**\n\n```c\nvoid moveZeroes(int* nums, int numsSize) {\n    int slow = 0;\n    for (int fast = 0; fast < numsSize; fast++) {\n        if(nums[fast]!=0){\n            nums[slow++] = nums[fast];\n        }\n    }\n    for(int i=slow;i<numsSize;i++){\n        nums[i] = 0;\n    }\n}\n```\n\n这里有DeepSeek的提点 才会的 \n\n我是之前用过 **双指针法**的 但是没想起来。\n\n- **相向而行（左右指针）**：一个在头，一个在尾，常用来反转数组、二分查找。\n\n- **同向而行（快慢指针）**：一个负责探路找活干(fast)，一个负责在原位按顺序接收(slow)。链表里找中点、找环，用的也是这个套路。\n",
            "url": "https://kyriechao.github.io/posts/2026/10/22-diary",
            "title": "2026-10-22 状态依旧 但是有小阻碍",
            "summary": "就业课写简历真的要我命 还没定稿 虽说勉强合格 但是我还会继续改改的 作业还加了 项目介绍、个人介绍等等好多",
            "date_modified": "2026-10-22T01:37:31.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/emoji",
            "content_html": "\n### 1. 常用提示模板\n```text\n⚠️ 注意\n⚠️ 警告\n⚠️ 风险提示\n❗ 重要\n❗️ 重点\n❕ 提示\n❓ 疑问\n❔ 待确认\n⁉️ 注意\n‼️ 重要\nℹ️ 信息\n💡 提示\n📌 重点\n📍 位置\n📝 备注\n📎 附件\n🔖 标记\n🔔 通知\n🔕 免打扰\n📢 公告\n📣 通知\n🚨 紧急\n🆘 求助\n✅ 成功\n☑️ 已完成\n✔️ 正确\n❌ 失败\n✖️ 错误\n❎ 取消\n⛔ 禁止\n🚫 不允许\n🆗 可以\n🆕 新增\n🔥 热门\n🚧 施工中\n🛑 停止\n```\n\n### 2. 警告/注意/信息类图标\n```text\n⚠️ ⚠ ❗ ❗️ ❕ ❓ ❔ ⁉️ ⁉ ‼️ ‼ ℹ️ ℹ 💡 🚨 🚩 🔔 🔕 📢 📣 💬 💭 🗯️ 🆘 🛑 ⛔ 🚫\n```\n\n### 3. 成功/失败/状态类图标\n```text\n✅ ☑️ ✔️ ✔ ❌ ✖️ ✖ ✗ ❎ 🆗 🆕 🔴 🟠 🟡 🟢 🔵 🟣 ⚫ ⚪ 🟤 🟥 🟧 🟨 🟩 🟦 🟪 ⬛ ⬜ 🟫\n```\n\n### 4. 重点/标记/文档类图标\n```text\n📌 📍 📎 🔖 🏷️ 📝 ✏️ 🖊️ 🖍️ 📋 📄 🗒️ 📅 📆 🗓️ ⏰ ⏳ ⌛ ⏱️ ⏲️ 🕰️ 🔍 🔎 🔒 🔓 🔑 🗝️ ⚙️ 🛠️ 🔧 🔨\n```\n\n### 5. 危险/天气/警示类图标\n```text\n🚧 🧨 💥 🔥 ⚡ ☠️ ☢️ ☣️ 🌩️ 🌧️ ❄️ 🌀 🌊 🚦 🚥\n```\n\n### 6. 几何符号/项目符号\n```text\n⚠ ℹ ※ ★ ☆ ● ○ ■ □ ▲ ▼ ◆ ◇ ◉ ◎ 🔶 🔷 🔸 🔹 🔺 🔻 💠 🔘 → ← ↑ ↓\n```\n\n### 7. 最常用\n```text\n⚠️ ❗️ ❓ ⁉️ ‼️ 🚨 ⛔ 🚫 ✅ ❌ ℹ️ 💡 📌 🔔 📢 🚧 🆘 🆗 🔥\n```\n\n小提示：  \n- 直接复制字符即可，Markdown 里不需要特殊语法。  \n- 有些平台会把 emoji 显示成黑白或方块，这是字体/平台支持问题。  \n\n\n",
            "url": "https://kyriechao.github.io/posts/2026/10/emoji",
            "title": "2026-10-22 emoji收集",
            "summary": "我觉得我的md不够丰富 符号合集 收集起来了",
            "date_modified": "2026-10-22T01:37:31.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/21-diary-rewritten",
            "content_html": "\n## 给自己的学习规则\n\n先阅读文档，弄懂基本概念；然后合上文档，试着独立写一个最小程序。如果不知道从哪里开始，就重新查文档，但要先想清楚自己具体卡在了哪里。\n\n最后记下一个明确的结果：程序成功做到了什么，或者自己停在了哪一步。\n\n## 今天的答辩和状态\n\n今天参加了 MCU 阶段性项目答辩，现场有不少完成度很高、也很有实力的项目。可惜我自己的项目没有做出来。前几天状态比较低迷，我对完成项目也渐渐没了期待。\n\n我发现，做自己真正感兴趣的事情时，学习会更快，进步也更明显。看到其他人的项目后，我并没有觉得自己在代码方面不如他们，甚至有些傲慢：我总觉得他们还在入门阶段，而自己已经写了好几年代码。\n\n不过，这次我确实把之前落下的内容补了起来，至少现在能自己敲出来，也开始记住一些 API 了。这是实实在在的进步。\n\n## 又差点开始做新项目\n\n今天写代码时，我发现自己差点又忍不住跑去折腾项目了。比如 Krio：我想让它在服务器上把文件复制或移动到 AS 的部署目录里，这样我就能在网页上复制文件内容。\n\nKrio 最初是为了解决一个很具体的问题：通过 Xshell 连接服务器时，我不方便复制长文件，手动复制最多只能处理大约 38 行。于是我做了这个工具，把文件放到网站的部署目录，再通过网页查看并点击复制。\n\n网站地址：[https://as.chaokyrie.asia](https://as.chaokyrie.asia)\n\n现在这个功能只有我自己能用。我还想把它扩展成注册用户也能上传文件，不过暂时先记下这个想法，不急着动手。\n\n另一个想法是给 CKit 加入静态库和共享库支持，让它用起来更方便。但我打算先等等：过几天如果还是觉得有必要，再开始做。很多想法都只有三分钟热度；即使做完了，如果实际价值不高，投入的时间也未必划算。\n\n## 力扣：杨辉三角 II\n\n下面是 DeepSeek 给出的解法：\n\n```c\n/**\n * Note: The returned array must be malloced, assume caller calls free().\n */\nint* getRow(int rowIndex, int* returnSize) {\n    int num = rowIndex + 1;\n    int* arr = malloc(sizeof(int) * num);\n    *returnSize = num;\n    for (int i = 0; i < num; i++)\n        arr[i] = 1;\n\n    for (int i = 2; i < num; i++) {\n        for (int j = i - 1; j > 0; j--) {\n            arr[j] = arr[j] + arr[j - 1];\n        }\n    }\n    return arr;\n}\n```\n\n下面是我自己写的版本。我的思路是递归地先拿到上一行，再计算当前行；不过一开始没发现递归参数如果写成 `num - 1` 就会一直求同一行。DeepSeek 指出这个问题后，我才意识到应该传入 `num - 2`，这样才能得到上一行。改好之后，代码就能按这个思路运行了。\n\n```c\n/**\n * Note: The returned array must be malloced, assume caller calls free().\n */\nint* getRow(int rowIndex, int* returnSize) {\n    int num = rowIndex + 1;\n    int* arr = malloc(sizeof(int) * num);\n    arr[0] = arr[num - 1] = 1;\n    *returnSize = num;\n    if (num < 2) {\n        return arr;\n    }\n    int n1;\n    int* brr = getRow(num - 2, &n1); // 这里 num -1 会死循环\n    // 这里我想拿到的是arr的上一层 num是rowIndex + 1\n    // num -1 一直求rowIndex的\n    // 没有求到rowIndex-1的 所以 疏忽了\n    for (int i = 1; i < num - 1; i++) {\n        arr[i] = brr[i - 1] + brr[i];\n    }\n    free(brr);\n    return arr;\n}\n```\n\n回头看了最近几天的日志，发现自己写了不少东西。我猜这和 Blog 变好看了也有关系：页面舒服了一些，我就更愿意打开它、看看自己的记录，再接着写下去。\n\n今天完成了一天一题，也复习了 Day 1 到 Day 6。Day 7 和 Day 8 我也看了看，今天学的是多线程同步控制和死锁，这些内容还需要再多理解几遍。\n\n以后多发一些自己觉得有意思的图片吧。\n\n![原神](/images/2026/10/21/1.jpg)\n\n![原神](/images/2026/10/21/2.jpg)\n\n![原神](/images/2026/10/21/3.jpg)\n\n今晚虽然也挺饿，但状态很好，这点不舒服不算什么。\n\n又做完一道题了。题目虽然简单，但苍蝇再小也是肉。\n\n```c\n// 给定一个非负整数 num，反复将各个位上的数字相加，直到结果为一位数。返回这个结果。\n\n// 示例 :\n// 输入: num = 38\n// 输出: 2\n// 解释: 各位相加的过程为：\n// 38 --> 3 + 8 --> 11\n// 11 --> 1 + 1 --> 2\n// 由于 2 是一位数，所以返回 2。\n\nint addDigits(int num) {\n    int n = 0;\n    while (1) {\n        n += num % 10;\n        num /= 10;\n        if (num <= 0)\n            break;\n    }\n    if (n >= 10) {\n        return addDigits(n);\n    }else{\n        return n;\n    }\n\n}\n```\n",
            "url": "https://kyriechao.github.io/posts/2026/10/21-diary-rewritten",
            "title": "状态回来了，也更愿意记录了 Copilot 修改版",
            "summary": "今天答辩了 MCU 阶段性项目，也重新找回了学习和写代码的状态。",
            "date_modified": "2026-10-21T02:22:52.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/21-diary",
            "content_html": "\n## 规则\n\n阅读文档，理解基本概念。\n\n合上文档，自己写一个最小程序。实在不知道怎么开始，可以重新查阅文档，但先明确自己卡在哪里。\n\n记录一个具体结果：程序运行成功了什么，或者自己在哪一步卡住了。\n\n## 日常 \n\n今天答辩了MCU的阶段性项目 我看到好多有实力的项目 \n\n我没做出来 因为前几天状态不在 低迷了 对完成项目 不期待了 \n\n> ChatGPT:句子之间缺少连接 \n\n> 明确了叙述顺序：发生了什么 → 结果如何 → 原因是什么。\n\n> 可以改成 \n\n`今天进行了 MCU 阶段性项目答辩。我看到了不少完成度很高、也很有实力的项目。`\n\n`遗憾的是，我自己的项目没有完成。前几天我的状态比较低迷，逐渐失去了继续完成项目的期待。`\n\n\n然后我觉得 还是对自己有兴趣的方面捣鼓的话 学习的很快 进步也很快 我傲慢着看着他们。\n\n为什么我说我是傲慢的呢 我从始至终 没觉得有人有我的代码深度 都是刚入门的新手 我起码敲了有几年了。\n\n- 我这次是真的补了 之前的内容了 起码可以敲出来了 不看别的 能记住api了\n\n\n> ChatGPT: 你知道自己想表达什么，却没有把逻辑写完整\n\n> 做自己感兴趣的事情，学习效率更高。 \n\n> 看到其他人的项目后，你对自己的代码能力仍然有很强的自信，甚至带有傲慢。\n\n`我发现，如果研究的是自己真正感兴趣的东西，学习速度和进步速度都会更快。`\n\n`看到其他人的项目时，我并没有因此觉得自己不如他们。相反，我甚至带着几分傲慢看待他们。`\n\n\n今天我敲代码的时候发现 我差点又忍不住去弄项目了 例如 krio 在服务器充当把文件复制移动到 as/下的 然后我可以在网页复制文件内容了\n\n\n这个krio就是 解决了 Xshell连接服务器后 我无法复制长文件的内容 手动复制的话 最多复制38行 krio直接复制移动到 复制网址部署目录下 我就可以在网页点击复制\n\n网站是:[https://as.chaokyrie.asia](https://as.chaokyrie.asia)\n\n我想修改成任何人都可以往这里传 前提是有注册的 当前就是我能使用。\n\n> ChatGPT: 技术描述有时不够准确  \n\n> 你知道整个功能的来龙去脉，但表达时把问题、解决方案和实现过程混在了一起。\n\n> 技术写作尤其需要明确主体、操作、对象和结果。\n\n`Krio 最初是为了解决一个实际问题：通过 Xshell 连接服务器时，我无法方便地复制较长的文件内容，手动复制最多只能处理约 38 行。`\n\n`因此，我写了一个工具，可以将文件复制到网站的部署目录中。这样，我就能在网页上查看文件内容，并直接点击按钮复制。`\n\n`目前，这个功能只供我自己使用。我还想进一步修改它，让注册用户也能上传文件。`\n\n\n另一个就是 CKit的了 \n\n接触到 静态库和共享库 我想加进CKit里面 让CKit更加的方便 但是我觉得先别做 \n\n等过几天还是觉得需要的再做 好多都是三分钟热度 即使做成了 价值不高的话 时间利用率就低了\n\n\n### 力扣 \n这个是deep seek 给的答案\n```c\n/**\n * Note: The returned array must be malloced, assume caller calls free().\n */\nint* getRow(int rowIndex, int* returnSize) {\n    int num = rowIndex + 1;\n    int* arr = malloc(sizeof(int) * num);\n    *returnSize = num;\n    for (int i = 0; i < num; i++)\n        arr[i] = 1;\n\n    for (int i = 2; i < num; i++) {\n        for (int j = i - 1; j > 0; j--) {\n            arr[j] = arr[j] + arr[j - 1];\n        }\n    }\n    return arr;\n}\n```\n\n这个是我自己写的 额 还是说一下 我没发现 num-1会无限递归 deepseek看了之后我才知道的 但是我思路的代码成功了\n```c\n/**\n * Note: The returned array must be malloced, assume caller calls free().\n */\nint* getRow(int rowIndex, int* returnSize) {\n    int num = rowIndex + 1;\n    int* arr = malloc(sizeof(int) * num);\n    arr[0] = arr[num - 1] = 1;\n    *returnSize = num;\n    if (num < 2) {\n        return arr;\n    }\n    int n1;\n    int* brr = getRow(num - 2, &n1); // 这里 num -1 会死循环  \n    // 这里我想拿到的是arr的上一层 num是rowIndex + 1 \n    // num -1 一直求rowIndex的\n    // 没有求到rowIndex-1的 所以 疏忽了\n    for (int i = 1; i < num - 1; i++) {\n        arr[i] = brr[i - 1] + brr[i];\n    }\n    free(brr);\n    return arr;\n}\n```\n\n我看了几天的日志 写的挺多的 功劳应该在我的Blog变好看的原因吧 愿意看愿意写了。\n\n> ChatGPT: 有些句子保留了思考过程，却没有完成最终表达\n\n> 这句话其实很有意思：你发现自己最近更愿意写日志，并猜测博客变好看可能是其中的原因。\n\n> 但“功劳应该在我的 Blog 变好看的原因吧”存在明显的语病。\n\n`回头看了最近几天的日志，发现自己写了不少内容。`\n\n`我觉得，这可能和 Blog 变得更好看有关。页面变得更舒服了，我也更愿意打开它、阅读自己的记录，并继续写下去。`\n\n今天完成了一天一题，复习了day1-day6 day7 day8也看了看 今天讲了 多线程同步控制、死锁还需要多看看多理解理解。\n\n\n多发发 我觉得有意思的图片吧\n\n![原神](/images/2026/10/21/1.jpg)\n\n![原神](/images/2026/10/21/2.jpg)\n\n![原神](/images/2026/10/21/3.jpg)\n\n\n虽然今晚也是挺饿的 但是我感觉状态很好  这个不足为惧。\n\n又解决一道 虽然很简单 但是苍蝇再小也是肉\n```c\n// 给定一个非负整数 num，反复将各个位上的数字相加，直到结果为一位数。返回这个结果。\n\n// 示例 :\n// 输入: num = 38\n// 输出: 2 \n// 解释: 各位相加的过程为：\n// 38 --> 3 + 8 --> 11\n// 11 --> 1 + 1 --> 2\n// 由于 2 是一位数，所以返回 2。\n\nint addDigits(int num) {\n    int n = 0;\n    while (1) {\n        n += num % 10;\n        num /= 10;\n        if (num <= 0)\n            break;\n    }\n    if (n >= 10) {\n        return addDigits(n);\n    }else{\n        return n;\n    }\n\n}\n```",
            "url": "https://kyriechao.github.io/posts/2026/10/21-diary",
            "title": "2026-10-21 状态回归或者",
            "summary": "状态回归或者 今晚状态特别好 写什么会什么",
            "date_modified": "2026-10-21T02:22:52.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/20-diary",
            "content_html": "\n\n## 日常\n\n害 又悠闲又忙 也不知道在忙什么。\n\n明天下午要答辩项目 简历也要改 准备PPT。\n\n但是我现在在写C语言 但是写的/练的也不全。\n\n我现在不情愿的把任务勾好了、我感觉我不会做。\n\n今天也是无趣。晚上还要带电脑回去、供暖预备呢好像。\n\n感觉 我知道我现在的处境或者说 当前处境还不够痛 不足以让我改变。一直拖拖拖。",
            "url": "https://kyriechao.github.io/posts/2026/10/20-diary",
            "title": "2026-10-20 无聊的一天",
            "summary": "什么都没做 什么都很急 ",
            "date_modified": "2026-10-20T12:07:29.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/19-diary",
            "content_html": "\n\n> 这里就先不学了已经顾不上了 项目跟学习进度有够我吃的了。\n\n## 日常\n\n今天真的是我的最倒霉的一天...\n\n早上一来 我开始弄我的项目小车烧完程序之后 抬头显示屏显示无信号 我疑惑怎么突然关机了\n\n我弄我的电脑 发现大概是坏了 去维修跟我说 主板烧坏了 花了我350 + 清洁/硅胶 100 450 我是从花呗里拿了\n\n上午没听课 我直接回宿舍了\n\n![随拍](/images/2026/10/19/1.jpg)\n\n我发现 不上课的时间 外面是这种情况么\n\n似乎我应该好好感受。\n\n晚上，电脑修好了 我就继续弄项目小车，但是我开始畏手畏脚了 我怕哪个操作又把电脑干坏了。\n\n到烧写代码时候 发现Keil MDK未发现设备？ 我就捣鼓就是没摸过芯片 后来我摸了一下 特别特别烫 左手中指指腹烫出了个小泡\n\n我在想的是 小车应该做不成了 害。\n\n今天学习了线程相关的锁。有互斥锁、自旋锁 里面还分阻塞/不阻塞。也是需要好好看看。\n\n现在感觉有点饿了，萌生出不想写日志、补任务了。但是！我决定补！\n\n太饿了 要走了！\n",
            "url": "https://kyriechao.github.io/posts/2026/10/19-diary",
            "title": "2026-10-19 电脑历险",
            "summary": "真的是吐了 今天收获颇多",
            "date_modified": "2026-10-19T13:08:35.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/17-weekly-plan",
            "content_html": "\n## 规则\n\n阅读文档，理解基本概念。\n\n合上文档，自己写一个最小程序。实在不知道怎么开始，可以重新查阅文档，但先明确自己卡在哪里。\n\n记录一个具体结果：程序运行成功了什么，或者自己在哪一步卡住了。\n\n## 本周重点\n\n### P0：Linux 系统编程\n- [X] 完成 Day 1–6 的系统复习\n- [X] 独立完成文件 I/O、fork/waitpid、管道通信实验 [原因](./20-diary) [原因](./21-diary) \n- [X] 整理知识盲点与待解决问题\n\n### P1：C 语言\n- [X] 持续进行力扣练习\n- [X] 复盘错题与无法独立完成的题目\n- [X] 针对薄弱点进行练习\n\n### P2：MCU 与嵌入式\n- [x] 保持适量练习\n- [x] 根据实际进度决定是否扩展学习[原因](./19-diary)\n\n\n## 本周约束\n- 周日以休息为主\n- 根据课程安排调整每日任务\n- 不为完成清单而牺牲理解质量\n\n## 周末复盘\n- 实际完成：\n- 未完成及原因：\n- 本周新发现：\n- 下周需要继续的任务：\n",
            "url": "https://kyriechao.github.io/posts/2026/10/17-weekly-plan",
            "title": "2026-10-19 周规划",
            "summary": "安排本周的学习重点、实践任务与阶段检查",
            "date_modified": "2026-10-18T16:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/18-diary",
            "content_html": "\n## 日常\n\n今天上了一天的就业课 我对着老师的教法不认同，或者说我对应试技巧的有了新的看法。\n\n今天教我们的方法有看公司网站已经做好的项目模块把这个项目当作自己在这家公司实习项目\n\n约等于我在这家公司实习过。\n\n教学期间能看的项目一点没有，该就业的时候 搞个速成的 不理解。\n\n今天调整了Bolg的图片文案。\n\n昨晚就打算什么都不做了。\n\n当作周一的吧规划吧",
            "url": "https://kyriechao.github.io/posts/2026/10/18-diary",
            "title": "2026-10-18 做简历",
            "summary": "做简历 我觉得快吐了",
            "date_modified": "2026-10-18T10:57:57.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/17-diary",
            "content_html": "\n## 日常\n\n今天重构了两个项目 `Plan`和`Blog` 几乎一天没听课、但是我感觉我学过一点的原因 重新拾起并不难。\n\n今天学`Thread`了、现在的进度线程学起来还是很简单的。\n\n周末不休息 我打算回去了 今天几乎什么都没做。\n\n\n**完成 P1：C 语言练习**\n```C\n/*\n  给定一个非负整数 numRows，生成「杨辉三角」的前 numRows 行。\n  在「杨辉三角」中，每个数是它左上方和右上方的数的和。\n*/\n\nint** generate(int numRows, int* returnSize, int** returnColumnSizes) {\n    *returnSize = numRows;\n    if (numRows == 0) {\n        *returnColumnSizes = NULL;\n        return NULL;\n    }\n\n    int** res = malloc(sizeof(int*) * numRows);\n    *returnColumnSizes = malloc(sizeof(int) * numRows);\n\n    for (int i = 0; i < numRows; i++) {\n        int cols = i + 1;\n\n        (*returnColumnSizes)[i] = cols;\n        res[i] = malloc(sizeof(int) * cols);\n\n        res[i][0] = 1;\n        res[i][cols - 1] = 1;\n\n        for (int j = 1; j < i; j++) {\n            res[i][j] =\n                res[i - 1][j - 1] + res[i - 1][j];\n        }\n    }\n\n    return res;\n}\n```\n> 这里我的思维是对的 但是就是写不出来、我第一个想出来的是递归，第二个才是循环。\n> 我应该先把最简单的想出来，后面再补高效的方法。\n\n我发现我对已知的东西会出现蔑视、傲慢 但是傲慢真的无从得知。\n\n如果我对我已经会知识重复 学习/观看的话 我会感觉浪费，所以这里很矛盾 该也不是不该也不是。\n\n**P0、P2没做。**\n明日的规划先不做、明天上就业课尽量做晚。\n\n\n\n\n**ChatGPT:**\n## 日常\n\n今天重构了两个项目：`Plan` 和 `Blog`，几乎一整天都在处理自己的项目，没怎么听课。\n\n我发现，之前接触过的东西，重新拾起来并没有想象中那么困难。至少对目前接触过的部分来说，重新理解和修改代码的成本还在可接受的范围内。\n\n不过，这也让我想到一个问题：重新拾起不难，不代表自己已经真正掌握。能看懂之前写的代码，和不看参考独立实现，还是两回事。\n\n今天学习了 `Thread`，目前感觉线程部分学起来比较简单。后面还需要通过代码验证自己是否真的理解，而不只是听懂了老师的讲解。\n\n原本今天计划复习 Linux 系统编程，并进行 C 语言练习和嵌入式巩固。实际完成的主要是两个项目的重构，以及一道力扣题。\n\n**P1：C 语言练习——杨辉三角**\n\n```C\n/*\n  给定一个非负整数 numRows，生成「杨辉三角」的前 numRows 行。\n  在「杨辉三角」中，每个数是它左上方和右上方的数的和。\n*/\n\nint** generate(int numRows, int* returnSize, int** returnColumnSizes) {\n    *returnSize = numRows;\n    if (numRows == 0) {\n        *returnColumnSizes = NULL;\n        return NULL;\n    }\n\n    int** res = malloc(sizeof(int*) * numRows);\n    *returnColumnSizes = malloc(sizeof(int) * numRows);\n\n    for (int i = 0; i < numRows; i++) {\n        int cols = i + 1;\n\n        (*returnColumnSizes)[i] = cols;\n        res[i] = malloc(sizeof(int) * cols);\n\n        res[i][0] = 1;\n        res[i][cols - 1] = 1;\n\n        for (int j = 1; j < i; j++) {\n            res[i][j] =\n                res[i - 1][j - 1] + res[i - 1][j];\n        }\n    }\n\n    return res;\n}\n```\n\n这道题的思路并不复杂：每一行的首尾都是 `1`，中间元素由上一行相邻的两个元素相加得到。\n\n我先想到的是递归，之后才想到循环。虽然最后的算法思路是正确的，但从思路转化成完整代码时仍然不够熟练。\n\n这让我意识到，算法思路正确，并不意味着自己已经具备熟练的实现能力。循环边界、数组访问、内存分配和特殊输入，都需要在实际编码中逐一处理。\n\n以后遇到类似的问题，我应该先写出最直接、容易验证的正确解，再考虑有没有更好的实现，而不是一开始就追求复杂或高效的方案。\n\n## 关于学习\n\n我发现自己面对已经学过的知识时，有时会觉得重复学习是在浪费时间。\n\n这可能是合理的效率判断，也可能是高估了自己的掌握程度。我暂时不能确定是哪一种。\n\n但我可以换一种检验方式：当我认为自己已经掌握某个知识点时，先尝试闭卷复述、独立编码或解决一道相关问题。如果能完成，就继续往前；如果不能，就回头补足缺口。\n\n与其反复判断自己是不是傲慢，不如让实际表现来检验自己的判断。\n\n## 今日复盘\n\n- **完成：** 重构 Plan 和 Blog；完成一道杨辉三角题；学习 Thread。\n- **未完成：** Linux 系统编程复习、嵌入式巩固。\n- **主要偏差：** 今天把大量时间投入了项目重构，原定学习任务没有完成。\n- **需要改进：** 做项目前先明确当天最重要的学习任务，避免临时投入挤占原定安排。\n\n明天有就业课，学习时间需要根据课程安排调整。即使不能执行完整的学习计划，也应该提前确定当天最重要的一项任务，并在结束后简单复盘。\n\n\n跟ChatGPT比对之后我就是个小菜鸡，我也会持续学习md文档编写、Git操作与词语运用的。\n\n",
            "url": "https://kyriechao.github.io/posts/2026/10/17-diary",
            "title": "2026-10-17 日志 重返Blog",
            "summary": "今天重构了plan.chaokyrie 和Blog",
            "date_modified": "2026-10-17T10:27:06.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/17-half-month-plan",
            "content_html": "\n## 规则\n\n阅读文档，理解基本概念。\n\n合上文档，自己写一个最小程序。实在不知道怎么开始，可以重新查阅文档，但先明确自己卡在哪里。\n\n记录一个具体结果：程序运行成功了什么，或者自己在哪一步卡住了。\n\n## 本阶段目标\n\n### 1. Linux 系统编程\n- [ ] 复习并掌握 Day 1–6 的核心知识\n- [ ] 完成文件 I/O、进程创建与回收、管道通信实验\n- [ ] 根据课程进度继续学习线程及相关知识\n\n### 2. C 语言\n- [ ] 持续进行力扣练习\n- [ ] 针对薄弱知识点进行专项训练\n- [ ] 提高独立编写、调试和排查问题的能力\n\n### 3. MCU 与嵌入式\n- [ ] 保持已有知识的练习\n- [ ] 根据实际进度进行扩展学习\n\n## 阶段原则\n- 优先处理有明确截止时间的任务\n- 先保证理解与实践，再追求学习进度\n- 根据实际情况调整目标，不机械地执行原计划\n\n## 阶段复盘\n- 完成情况：\n- 未完成事项及原因：\n- 新发现的问题：\n- 下一阶段的调整：\n",
            "url": "https://kyriechao.github.io/posts/2026/10/17-half-month-plan",
            "title": "2026-10-17 半月规划",
            "summary": "明确未来半个月的主要目标与阶段成果",
            "date_modified": "2026-10-17T04:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/17-daily-plan",
            "content_html": "\n## 明日目标\n\n### P0：Linux 系统编程复习\n\n- [X] 不看笔记，梳理 Day 1—Day 6 的核心知识\n- [X] 复习文件 I/O：`open`、`read`、`write`、`lseek`\n- [X] 编写文件读写实验，处理错误返回值\n- [X] 编写 `fork` + `waitpid` 实验，理解进程创建与回收\n- [ ] 编写匿名管道实验，完成父子进程通信\n- [ ] 记录无法独立解释或实现的知识点\n\n### P1：C 语言练习\n\n- [X] 完成 1—2 道力扣题\n- [X] 复盘解题思路、边界条件和代码问题\n\n### P2：嵌入式巩固\n\n- [ ] 如果前两项完成且精力允许，进行 MCU 练习或复习\n\n## 明日安排\n\n1. 先闭卷回忆 Day 1—Day 6，列出知识盲点。\n2. 优先完成 Linux 实验，验证自己是否真正理解。\n3. 进行 C 语言练习。\n4. 根据剩余时间决定是否练习 MCU。\n\n## 明日复盘\n\n- **完成了什么：**\n- **没完成什么，原因是什么：**\n- **今天发现的知识盲点：**\n- **明天需要继续什么：**\n",
            "url": "https://kyriechao.github.io/posts/2026/10/17-daily-plan",
            "title": "2026-10-17 日规划",
            "summary": "复习 Linux 系统编程前六天内容，并通过实验检查掌握程度",
            "date_modified": "2026-10-17T02:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/10/13-diary",
            "content_html": "\n## 技术\n\n上次写日志内容是学到SPI 现在是学完MCU之后、进到Linux高级编程了。 明晚考MCU 我一直在背相关知识点呢\n\n今天把新的板子画好了也下单了、给我感觉是我在慢慢的会、板子也知道大概怎么画了。\n\n我的目标并不是一方面很强、我希望我在计算机各各领域都有不错的掌握。\n\n最近写了好几个项目了 但是给我的感觉就像玩具似的、一个是压缩工具、另一个是图像处理工具 都是CLI\n\n硬件也是有的 有一个小狗 但是并不是我完全自己焊接出来的 而是组装的 我现在对硬件还不是很有信心 所以我只能慢慢来。\n\n\n\n## 思考\n\n感觉好多 但是今天先不讲了 昨天熬夜做了个东西 今晚得早点回去 洗衣服 洗澡。\n\n## 明日计划\n1. 跟上课程知识点\n2. Kria慢慢探索\n3. 保持平静",
            "url": "https://kyriechao.github.io/posts/2026/10/13-diary",
            "title": "2026-10-13 回归",
            "summary": "好多天没写日志了 也不是不愿意写，只是最近比较忙",
            "date_modified": "2026-10-13T12:46:21.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/09/23-diary",
            "content_html": "\n## 技术\n\n### C 语言\n今天感觉学习SPI、感觉格外好。也不知道为什么、今天跟着老师画新的板子 这次一定要成功。\n\n练习完BST了、感觉会了也不会似的。明天默写一次看看。\n\n### Rust\n无\n## 思考\n无 \n\n## 明日计划\n\n继续 ",
            "url": "https://kyriechao.github.io/posts/2026/09/23-diary",
            "title": "2026-09-23 学习SPI",
            "summary": "学习SPI",
            "date_modified": "2026-09-23T14:08:12.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/09/22-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n今天学了OLED的驱动,复习之前的MCU知识。今天也是没做其他的东西。真正的放松了一点。\n\n### Rust\n无。\n## 思考\n\n我开始意识到、我对边界感的把控并不是很好。 我对任务处理很急、但是分不清是不是我的任务。 我需要冷静下来、理清任务的优先级。 我需要学会说不、学会拒绝一些任务。 我需要学会时间管理、学会分配时间。 我需要学会任务分解、学会将大任务分解成小任务。 我需要学会任务优先级排序、学会先做最重要的任务。 我需要学会任务时间限制、学会为每个任务设定时间限制。 \n\n我应该开始注意这个方面。\n\n## 明日计划\n1. C语言强化训练\n2. 听课 现在已经进入我不懂的知识里了不能在怠慢的观望。",
            "url": "https://kyriechao.github.io/posts/2026/09/22-diary",
            "title": "2026-09-22",
            "summary": "\n 技术\n\n C 语言\n\n今天学了OLED的驱动,复习之前的MCU知识。今天也是没做其他的东西。真正的放松了一点。\n\n Rust\n无。\n 思考\n\n我开始意识到、我对边界感的把控并不是很好。 我对任务处理很急、但是分不清是不是我的任务。 我需要冷静下来、理清任务的优先级。 我需要学...",
            "date_modified": "2026-09-22T13:15:36.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/09/21-diary",
            "content_html": "\n## 技术\n\n### C 语言\n做完了 PixLab、老师让我测试平常的图片 我只做了固定的24位的 我在考虑要不要做。\n善恶的彼岸读了一点、感觉没有新的思想。\n### Rust\n\n## 思考\n\n## 明日计划\n1. 复习 MCU",
            "url": "https://kyriechao.github.io/posts/2026/09/21-diary",
            "title": "2026-09-21 日记",
            "summary": "日记",
            "date_modified": "2026-09-21T13:43:19.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/09/20-diary",
            "content_html": "\n## 技术\n\n### C 语言\n今天学了MCU ADC 感觉不难。结束就是有点饿了、导致效率下降。明天补MCU知识点 防止突击考试。\n\n### Rust\n无\n## 思考\n无\n## 明日计划\n\n1. PixLab继续\n2. 巩固MCU知识",
            "url": "https://kyriechao.github.io/posts/2026/09/20-diary",
            "title": "2026-09-20 PixLab开始",
            "summary": "PixLab开始",
            "date_modified": "2026-09-20T13:00:36.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/09/16-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\nComp-Chao 阶段性结束。\n开启 PixLab 慢慢写！\n\n### Rust\n无\n## 思考\n\n\n# 关于“我有没有变”与人与人关系的思考\n\n### 一、我最近为什么觉得自己在被“同化”\n\n最近几个星期，我又过度投入项目。\n\n这次的 Comp-Chao 本身是老师布置的作业，不是最初出于自己的兴趣，但做着做着也产生了兴趣。老师说项目可以持续迭代、优化越多越好，于是自己不断往里面投入。\n\n但慢慢发现，项目占据了越来越多的时间：\n\n* 每天日志变少了\n* C 语言训练被挤压\n* 《善恶的彼岸》没有继续好好读\n* 原本的计划没有执行\n* 和自己真正思考、交流的时间越来越少\n\n于是产生一种很中二但很准确的感觉：\n\n> **好像正在被某种东西一点点同化。**\n\n就像自己原本有一套东西——思考、创造、学习、探索、自我认识——但每天被任务和项目消耗以后，这些东西逐渐变轻。\n\n---\n\n### 二、真正让我难受的，不只是项目，而是对“自我能力”的怀疑\n\n压缩项目让我第一次非常明显地意识到：\n\n> **这个项目到底有多少是我真正会的？**\n\n一开始，项目确实是自己写出来的，至少自己能理解、能跟着做。\n\n但到了后面，越来越多代码变成：\n\n> AI 带着自己写 → 自己看 → 抄写、修改 → 项目继续向前。\n\n于是项目越来越完整，但自己的理解没有以同样的速度增长。\n\n这导致了一个非常难受的落差：\n\n> **以前我写出来的东西，真的是我写的吗？**\n\n甚至开始怀疑：\n\n> “其实我什么都不会。”\n\n但更准确的说法应该是：\n\n> **项目的后半部分，我跳过了“理解 → 独立实现”这一过程。**\n\n所以这半个月并不是完全没有价值。\n\n获得了项目经验、C 的实际经验、哈夫曼树、BMP、图像处理、AI 辅助开发等东西。\n\n但它确实没有产生自己原本期待的那种能力提升：\n\n> **“我能够脱离 AI，自己从需求开始写出来。”**\n\n这也是这次项目真正暴露的问题。\n\n---\n\n### 三、我对 MCU 的感觉也是一样\n\n机构里学 MCU 一周，听老师讲的时候感觉：\n\n> “好像也没什么难的。”\n\n但是让自己脱离老师、从零开始写：\n\n> **又不能完全写出来。**\n\n这让我意识到：\n\n**看懂 ≠ 能回忆 ≠ 能独立写出来。**\n\n学习大致存在几个阶段：\n\n```text\n看懂\n ↓\n跟着写\n ↓\n修改\n ↓\n脱离示例自己写\n ↓\n自己定位问题\n ↓\n从需求开始设计\n```\n\n现在自己开始意识到，过去可能经常把“看懂”误认为“掌握”。\n\n这次项目让这个区别变得非常明显。\n\n---\n\n# 四、我开始怀疑：是不是自己的“初心”越来越轻了\n\n过去的自己似乎更有一种感觉：\n\n> **我可以学会新的东西。**\n\n新语言、新技术、新项目、新感觉，都愿意尝试。\n\n这种自信不是：\n\n> “我什么都会。”\n\n而是：\n\n> **“虽然我现在不会，但我相信自己能够学会。”**\n\n而最近这种感觉似乎越来越弱。\n\n项目做得越久，反而越不确定自己。\n\n但进一步想，也许不是初心真的消失了。\n\n更可能是：\n\n> **执行模式占据了太多时间，反思模式没有空间了。**\n\n以前会思考：\n\n* 我为什么喜欢创造？\n* 我到底想成为什么样的人？\n* 我怎么看待自己？\n* 我为什么学习？\n* 我与别人是什么关系？\n* 我正在发生什么变化？\n\n而最近更多是：\n\n```text\n老师任务\n↓\n写代码\n↓\nDebug\n↓\nAI\n↓\n优化\n↓\n提交\n↓\n继续\n```\n\n一直在解决：\n\n> **“下一步怎么办？”**\n\n却越来越少问：\n\n> **“我为什么要走这条路？”**\n\n---\n\n# 五、我开始重新理解“人的性格会不会改变”\n\n我提出了一个判断：\n\n> **人的性格是可以变的，但是也不会变。**\n\n后来用自己的经历去验证。\n\n高中时候的自己：\n\n* 很皮\n* 爱笑\n* 比较活跃\n\n进入大学以后：\n\n* 变得拘束\n* 不爱说话\n* 显得沉稳\n\n到了机构以后，同桌却说：\n\n> **“你很皮。”**\n\n这让我突然意识到：\n\n> **我可能根本没有发生想象中的彻底变化。**\n\n只是不同环境下，我选择表现出了不同的一面。\n\n高中时，我允许自己“皮”。\n\n大学里，我可能更谨慎、更拘束，于是“沉稳”的自己占据了主导。\n\n到了机构，因为和身边的人逐渐熟悉、放松，原来的那一面又出现了。\n\n所以：\n\n> **性格不是一个固定的人格模板。**\n\n一个人可能同时拥有很多倾向，只是在不同环境、关系和人生阶段中，选择让哪一部分出来。\n\n因此，“变化”与“不变”可以同时成立：\n\n> **人的行为、习惯、表达方式可以变化；但某些更深层的倾向，可能一直存在。**\n\n例如一个人可能一直喜欢：\n\n> 探索、创造、独立、寻找意义。\n\n只是年轻时通过写小说表达，后来通过写程序、设计工具、设计语言表达。\n\n**媒介变了，内在的创造倾向却可能一直存在。**\n\n---\n\n# 六、我在人际关系里有一个反复出现的念头\n\n当自己和一个人逐渐相处得很好时，经常会突然产生一种冲动：\n\n> **“其实你不了解我。”**\n\n现在机构里也有关系不错的朋友。\n\n但想到他们12月底可能离开，就会产生一种念头：\n\n> **“我们离开之后，不会再像现在这么好了。”**\n\n甚至更加极端：\n\n> **“以后你就像没见过我似的。”**\n\n这背后可能不只是害怕失去朋友。\n\n更深层的问题是：\n\n> **现在如此真实的关系，会不会因为共同生活结束，而在未来变得像从来没有发生过？**\n\n---\n\n# 七、“你不了解我”可能有两层含义\n\n### 第一层\n\n> **你认识的只是我在这个环境里的一个切面。**\n\n朋友认识：\n\n* 机构里的自己\n* 自己的玩笑\n* 学习状态\n* 某些故事\n* 日常表现\n\n但他们不知道自己全部的经历、思考、恐惧、价值观。\n\n所以：\n\n> “你和我关系很好，但你其实只认识了我一部分。”\n\n这是事实。\n\n### 第二层\n\n更深的是：\n\n> **即使现在你了解我，以后你还会认识现在这个我吗？**\n\n因为人会变化。\n\n今天的自己和半年后的自己不会完全一样。\n\n所以自己实际上在问：\n\n> **如果一段关系依赖于共同经历，那么共同经历结束以后，这段关系还剩下什么？**\n\n---\n\n# 八、这里可能存在一个自己的习惯：提前站到未来看现在\n\n现在关系很好。\n\n但自己的意识已经开始想：\n\n> “以后肯定会淡。”\n\n于是人在当下，却已经站到了未来回头看这段关系。\n\n这可能导致一种提前抽离：\n\n> “反正以后都要分开。”\n\n但这里需要区分：\n\n> **以后不亲密 ≠ 现在不真实。**\n\n很多关系确实只存在于人生的某个阶段。\n\n高中同学、大学同学、培训机构的朋友、工作同事，都可能随着环境变化而逐渐疏远。\n\n但未来的疏远并不能证明现在的关系是假的。\n\n> **一段关系不需要持续一辈子，才能证明它曾经真实存在。**\n\n---\n\n# 九、目前最值得留下的一句话\n\n这几次思考其实可以汇成一句：\n\n> **我不是害怕自己改变，我真正害怕的是，在不断改变的过程中，逐渐认不出自己。**\n\n而现在需要做的，不是拼命证明：\n\n> “以前的我才是真正的我。”\n\n也不是强行找回所谓的“初心”。\n\n而是重新观察：\n\n```text\n哪些东西只是阶段性的？\n哪些东西是环境造成的？\n哪些东西是我主动选择的？\n哪些东西即使换了环境，我依然会去做？\n```\n\n也许真正的“初心”并不是某个固定版本的自己。\n\n而是那个更深的东西：\n\n> **即使我换了环境、换了技术、换了生活方式，我依然愿意探索、创造、学习，并且不断回来问自己：我到底在做什么？**\n\n这次暂停 Comp-Chao，本质上也不只是“少做一个项目”。\n\n而是给自己留一点空间，重新听见自己的声音。\n\n\n## 明日计划",
            "url": "https://kyriechao.github.io/posts/2026/09/16-diary",
            "title": "2026-09-16 是不是自己的“初心”越来越轻了",
            "summary": "是不是自己的“初心”越来越轻了",
            "date_modified": "2026-09-16T12:14:20.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/09/11-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n前几天进入了新的阶段 MCU，一直在搞板子中间我也在写压缩项目 昨天已经写完标准哈夫曼压缩的了。\n\n也是下单买了好多元器件，又是我买。我发现我自己有点傲慢 我以为自己会了C语言 MCU的代码 都是玩具似的。\n\n但是我昨天写按钮的延时去抖的代码 发现我虽然能看懂但是给别人解释搞不清楚 上拉/下拉 问了ai才知道。\n\n还是得好好听课。\n\n### Rust\n无\n## 思考\n无\n## 明日计划\n1. 修饰好压缩项目 短文件/大文件/目录 现在压缩适中长度的普通文件 还是可以的。\n2. 精通MCU代码",
            "url": "https://kyriechao.github.io/posts/2026/09/11-diary",
            "title": "2026-09-10 补日志",
            "summary": "好几天都没写日志了 现在补一下之前的进度",
            "date_modified": "2026-09-10T01:11:37.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/09/07-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n哈夫曼项目差不多完成了 但是也是通过ai写的其他细节。\n\n### Rust\n无\n## 思考\n无\n## 明日计划\n完善项目",
            "url": "https://kyriechao.github.io/posts/2026/09/07-diary",
            "title": "2026-09-07 压缩项目小成",
            "summary": "压缩项目小成 哈夫曼结构写完了",
            "date_modified": "2026-09-07T11:09:38.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/09/04-diary",
            "content_html": "\n## 技术\n\n\n### C 语言\n今天意识到写项目并非能很有效地学习C语言数据结构。\n\n今天靠Codex，温习链表真的自己写出来的一点没靠ai 打算今天练习完哈希+链表的。但是老师进度较快 安装了很多软件。\n\n所以不得不暂缓数据结构。\n\n明天继续通过Codex 训练自己的C语言数据结构。\n\n写一半才\n\n```c\n\n#include \"lua_demo.h\"\n#include <stdio.h>\n\n// LRU 缓存\nstruct node {\n    int data;\n    struct node *next, *prev;\n};\n\nstruct head {\n    Node* head;\n    Node* tail;\n    // size_t capacity;\n};\n\n// 初始化\nList* list_init() {\n    List* l = malloc(sizeof(List));\n    if (!l) return NULL;\n    Node* node = malloc(sizeof(Node));\n    if (!node) {\n        free(l);\n        return NULL;\n    }\n    node->prev = node->next = NULL;\n    l->head = node;\n    l->tail = NULL;\n    return l;\n}\nstatic void link_head(List* l, Node* n) {\n    Node* p = l->head->next;\n    n->next = p;\n    n->prev = l->head;\n    p->prev = n;\n    l->head->next = n;\n}\n// 头插\nint insert_head(List* l, int data) {\n    if (!l) return -1;\n    Node* n = malloc(sizeof(Node));\n    if (!n) return -1;\n    n->data = data;\n    n->next = n->prev = NULL;\n    if (l->head->next == NULL) {\n        l->head->next = n;\n        n->prev = l->head;\n        l->tail = n;\n        return 0;\n    }\n    link_head(l, n);\n    return 0;\n}\n\n// 取任意节点\nNode* fetch(List* l, int target) {\n    if (l == NULL) return NULL;\n    Node* p = l->head->next;\n    while (p != NULL && p->data != target) p = p->next;\n    if (p == NULL) return NULL;\n    return p;\n}\n\n// 删除尾节点\nint delete_tail(List* l) {\n    if (!l || !l->tail) return -1;\n    Node* p = l->tail;\n    p->prev->next = NULL;\n    if (p->prev == l->head)\n        l->tail = NULL;\n    else\n        l->tail = p->prev;\n    free(p);\n    return 0;\n}\n\n// 把一个已在链表中的节点移到头部（只改指针，不 malloc、不 free）\nint move_to_head(List* l, Node* node) {\n    if (!l || !node) return -1;\n    Node* prev = node->prev;\n    Node* next = node->next;\n    if (prev == l->head) return 0;\n    if (next == NULL) {\n        prev->next = NULL;\n        l->tail = prev;\n    } else {\n        prev->next = next;\n        next->prev = prev;\n    }\n    node->prev = node->next = NULL;\n    link_head(l, node);\n    return 0;\n}\n\n// 销毁\nint destroy(List* l) {\n    Node* p = l->tail;\n    if (p == NULL) {\n        free(l->head);\n        free(l);\n        return 0;\n    }\n    while (p != NULL) {\n        Node* tmp = p->prev;\n        free(p);\n        p = tmp;\n    }\n    free(l);\n    return 0;\n}\n\nvoid show(List* l) {\n    Node* p = l->head->next;\n    if (p == NULL) {\n        printf(\"NULL\\n\");\n        return;\n    }\n    while (p != NULL) {\n        printf(\"%d \", p->data);\n        p = p->next;\n    }\n    printf(\"\\n\");\n}\n\nint main() {\n    // int a;\n    // scanf(\"%d\", &a);\n    List* list = list_init();\n    insert_head(list, 5);\n    insert_head(list, 4);\n    insert_head(list, 3);\n    insert_head(list, 2);\n    insert_head(list, 1);\n    show(list);\n    // printf(\"%d\\n\", list->tail->data);\n    Node* p = fetch(list, 5);\n    move_to_head(list, p);\n    // show(list);\n    // destroy(list);\n    // insert_head(list, 1);\n    show(list);\n    // delete_tail(list);\n    // insert_head(list, 3);\n    // insert_head(list, 2);\n    // insert_head(list, 1);\n    // Node* p = fetch(list, 1);\n    // move_to_head(list, p);\n    // delete_tail(list);\n    // int n = delete_tail(list);\n    // printf(\"%d\\n\", n);\n    // show(list);\n    destroy(list);\n    return 0;\n}\n\n```\n\n- 图像处理工具（使用 BMP 图像格式，实现灰度化、负片\n老师之前发的让我做的项目：\n- 文件压缩工具（基于哈夫曼树实现）、模糊、边缘检测等）\n- 智能温控风扇、智能储物柜、智能密码锁等\n\n我对第一第二个项目很感兴趣，第三个是能做但感觉不是很难 感觉没什么技术含量。\n\n### Rust\n无\n## 思考\n无\n## 明日计划\n\n1.写完Lua的练习测试 跟住老师的进度同时巩固C语言数据结构。\n2.学习好MCU\n3.做项目（文件压缩工具、图像处理工具、智能温控风扇、智能储物柜、智能密码锁等）\n\n",
            "url": "https://kyriechao.github.io/posts/2026/09/04-diary",
            "title": "2026-09-04 进入新阶段MCU",
            "summary": "进入新阶段MCU",
            "date_modified": "2026-09-04T13:08:44.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/09/03-diary",
            "content_html": "\n## 技术\n\n### C 语言\n今天项目是一点没写，晚上突然考试，压力山大。\n\n我还在练习链表、栈、队列、哈希表 不是很如意。\n\n今天数据结构教完了，明天就进新内容了。\n\n今天思考到了设计编程语言这里 很投入我应该把想法写下来不是立即实现，以后再说。\n\n### Rust\n无\n## 思考\n无\n## 明日计划\n\n",
            "url": "https://kyriechao.github.io/posts/2026/09/03-diary",
            "title": "2026-09-03 突然考试 紧张死了",
            "summary": "突然考试 紧张死了",
            "date_modified": "2026-09-03T11:38:07.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/09/02-diary",
            "content_html": "\n## 技术\n\n### C 语言\n今天学了 二叉树 依旧学完简单。\n\n今天日志项目完结了，新开了一个项目\n\ntask.c / task.h\t0%\t未开始\n\nscheduler.c / scheduler.h\t0%\t未开始\n\nstorage.c / storage.h\t0%\t未开始\n\nmain.c\t0%\t未开始\n\n感觉还是摸不着头脑 不知道怎么写。\n\n今天读善恶的彼岸。\n\n### Rust\n无\n## 思考\n无\n## 明日计划\n写好项目 学习怎么构思写好c的项目。",
            "url": "https://kyriechao.github.io/posts/2026/09/02-diary",
            "title": "2026-09-02 普通的一天",
            "summary": "普通的一天",
            "date_modified": "2026-09-02T12:23:40.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/09/01-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n今天学了链表和队列。老师讲得挺好的，但是我感觉他还是讲的不够到位，但是对于小白来说，已经很清晰了。\n\nC Kit 真的是一点不动了。\n\n今天重新用VsCode 写代码发现，我的C Kit 真的不如不存在，但是我也不会后悔做这个项目的。\n\n### Rust\n无\n\n## 思考\n\n### 关于认识自我、痛苦与行动\n\n最近我开始重新思考一个问题：**我到底该怎样真正认识自己？**\n\n以前我经常思考自己，思考痛苦、快乐、人生、价值、未来，也会把这些思考写进每天的日志里。但这次我开始怀疑：**持续思考本身，是不是就等于成长？**\n\n我发现并不是。\n\n如果思考最终没有改变我的行为，它可能只是停留在脑子里的活动。真正认识自己，也不是不断给自己下定义，而是观察自己在现实中反复做出的选择。\n\n比如这次学习 C 语言。\n\n我本来应该学习 C，今天学完了链表，也接触到了队列。但我发现一个问题：今天刚学会的东西，第二天重新写的时候依然会卡壳。我会因此觉得重复很烦，觉得自己明明已经学过了，为什么还要再写。\n\n后来我意识到：\n\n**“知道”并不等于“能够独立写出来”。**\n\n第一次写可能只是理解，第二次、第三次甚至更多次重复，才是在把知识变成真正的能力。\n\n我过去可能太容易把“继续向前学新东西”当成进步，却忽略了重复、巩固和失败本身也是学习的一部分。\n\n---\n\n我也重新审视了 C-Kit。\n\nC-Kit 是我自己用 Go 写的一个 C 语言 CLI 工具，同时整合了 Vim 环境、clang-format、quickfix、ctags、补全等功能。现在它已经基本完结，后续最多修改 bug。\n\n回头看，我发现自己在写 C-Kit 的时候其实非常快乐。\n\n这让我意识到：\n\n**我并不是讨厌编程，也不是讨厌 C。**\n\n真正让我厌烦的，可能是那些重复、机械、没有创造感的部分。\n\n而创造自己的工具，会让我获得很强的掌控感和即时反馈。\n\n这也让我发现一个需要警惕的问题：\n\n> 当学习让我感到困难和不顺的时候，我很容易转向一个更有掌控感、更容易获得成就感的项目。\n\n例如：\n\n学习 C → 写得不顺 → 去完善 C-Kit → 获得快乐 → 更想继续优化 C-Kit → C 的学习进度反而被耽误。\n\n所以我需要区分：\n\n**我是在创造，还是在通过创造逃避“不熟练的自己”？**\n\n这个问题暂时没有必要立刻给自己下结论，而应该通过之后的实际行为去验证。\n\n---\n\n这次对话里，我还发现了另一个问题：\n\n我很容易认为很多事情都重要。\n\n现在的我可能同时觉得：\n\n> C 语言重要。\n> 嵌入式重要。\n> Rust 重要。\n> Linux 重要。\n> C-Kit 重要。\n> 系统重要。\n> 编程语言设计重要。\n> 软硬件也重要。\n\n每一个想法单独看都没有错。\n\n真正的问题是：\n\n**我很难接受“现在只能做其中一部分”。**\n\n所以最后就变成了：\n\n> 什么都舍不得放弃，什么都觉得应该做。\n\n而现实是：\n\n**可以同时喜欢很多东西，但不能同时认真推进所有东西。**\n\n因此我需要开始区分：\n\n### 主线\n\n当前阶段最重要、真正决定未来能力的事情。\n\n### 兴趣\n\n我喜欢研究、创造，但不应该抢走主线资源的事情。\n\n### 休息\n\n不以成长和产出为目的，只为了恢复状态的时间。\n\n这三种事情都可以让我快乐，但不能互相冒充。\n\n---\n\n我以前容易把“休息”理解成完全什么都不做。\n\n但现在我觉得，对我而言，休息未必一定是躺着发呆。\n\n**从当前任务切换到另一件低压力、没有考核的事情，同样可以是休息。**\n\n例如散步、玩游戏、看电影、做饭、研究一些完全无关紧要的小东西。\n\n关键不在于有没有行动，而在于：\n\n> **这件事情是在恢复我，还是只是让我换一个方向继续工作？**\n\n如果我嘴上说自己在休息，实际上却一直想着学习、优化项目、研究技术，那么我其实并没有真正退出“必须进步”的状态。\n\n---\n\n这次对话让我重新理解了“认识自我”。\n\n真正认识自己，也许不是一直问：\n\n> “我到底是一个什么样的人？”\n\n而是观察：\n\n> 我什么时候会逃避？\n> 我通常逃避到哪里？\n> 什么事情让我获得强烈的掌控感？\n> 什么事情虽然痛苦，但确实让我变强？\n> 什么事情只是让我当下舒服？\n> 我面对困难、失败、无聊和不确定性时，到底会怎么选择？\n\n**人格和自我认识，不应该只建立在自我描述上，也应该建立在长期行为模式上。**\n\n所以接下来，我不应该急着判断自己是懒惰、完美主义、爱思考，还是喜欢创造。\n\n我更应该观察自己。\n\n我尤其需要观察：\n\n> **当我不舒服的时候，我是在坚持一件值得坚持的事情，还是在逃避一件让我暴露自己“不够好”的事情？**\n\n这可能是我这次对话里最重要的问题。\n\n最后，我想提醒自己：\n\n> **不是所有痛苦都意味着方向错误，也不是所有快乐都意味着方向正确。**\n>\n> **短期的舒服，只能说明我现在感觉不错；长期反复的选择，才更接近真正的自己。**\n\n而现在的我，真正需要练习的可能不是“找到唯一正确的人生答案”，而是：\n\n**学会在众多都重要的事情里，选择当前最重要的一件，然后暂时允许其他事情排队。**\n\n\n## 明日计划",
            "url": "https://kyriechao.github.io/posts/2026/09/01-diary",
            "title": "2026-09-01 自我认识",
            "summary": "我开始意识到：我缺少的不是目标，而是取舍",
            "date_modified": "2026-09-01T12:23:15.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/3/31-diary",
            "content_html": "\n## 技术\n\n### C 语言\n最近一直在学链表 感觉君哥讲的方式怪怪的。链表节点不好好教 开始引入了信息结构体，有指向第一个节点的指针 有指向最后一个的指针 还有存链表长度的。\n\n也不知道这个教法能弄懂么？\n\n今天继续弄好了C Kit 之后再也不弄了 专心写好代码。\n### Rust\n无。\n## 思考\n无。\n## 明日计划\n日志系统 写好。",
            "url": "https://kyriechao.github.io/posts/2026/08/3/31-diary",
            "title": "2026-08-31 C Kit完结",
            "summary": "C Kit完结",
            "date_modified": "2026-08-31T12:57:25.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/2/26-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n#### 哈希表\n今天 自己写完了哈希表 但还是感觉掌握的不够。写完哈希表真的有种恍惚感、毕竟之前没有真的手搓过 顶多算过哈希表。\n\n#### 教学\n老师昨天和今天讲完了排序算法。(冒泡、选择、插入、快速) 除了快速有点不熟 其他前一个月就掌握了。\n\n明天老师应该会将链表了。但是我自己学到哈希表了。如果我今天才学到或者认识到排序的话、我可能学不会 温故而知新 不亦乐乎。\n\n#### 工具\n也是完善了C Kit 工具、写了新的两个工具使用文档 vim和git的。\n\n把文件复制 as 工具写了个网页 以后好复制系统里的文件。\n\n前几天用Go重构了之前用脚本写的run工具 新名为C Kit。\n\n\n## Rust\n感觉不学rust了似的 但是吧确实 C语言的数据结构这块掌握的不够。\n\n## 思考\n似乎日志我都没有自己认真写过，顶多就是抛给ai 今天写的代码和文档。\n所以决定 自己纯手写日志。\n\n## 明日计划\n\n1. 理解好哈希表 整理整个数据结构的知识点 明天写个小项目 验收我对链表、堆、栈、哈希表的学习。\n2. 勇敢活着",
            "url": "https://kyriechao.github.io/posts/2026/08/2/26-diary",
            "title": "2026-08-26 周三 哈希表",
            "summary": "所以决定 自己纯手写日志",
            "date_modified": "2026-08-26T12:39:45.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/2/24-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n学习内容：**结构体深入**；**共用体、枚举与 I/O**。\n\n#### 结构体深入（Day20）\n\n- **声明方式**：普通 `struct` 声明；`typedef` 声明（可同时声明指针类型，如 `typedef struct device {...} device, *pdev;`）；声明类型的同时定义变量；匿名结构体（无类型名，只能声明时直接定义变量）\n- **初始化**：完全初始化（按成员定义顺序）、局部初始化（只给前面部分，其余成员默认清零）、指定初始化 `.成员 = 值`（C99，gcc 支持，可乱序可跳过）\n- **访问**：变量/常量用成员运算符 `.`，指针用指向运算符 `->`；`(*p).m` 与 `p->m` 完全等效；同类型结构体可整体赋值 `b2 = b1`\n- **堆区结构体**：`malloc(sizeof(结构体类型))` 分配，用完 `free`\n- **内存对齐**：成员相对地址 = \"成员宽度与对齐系数较小值\"的整数倍；总大小 = \"最大成员宽度与对齐系数较小值\"的整数倍；数组成员按元素宽度对齐，嵌套结构体成员按其最大成员宽度对齐；64 位默认对齐 8，32 位默认 4，`#pragma pack` 可改 1/2/4/8\n- **结构体与函数**：强烈建议用指针做参数/返回值（值传递是整体拷贝，数据量大时开销大）；只读函数参数加 `const`；返回 malloc 的结构体由调用者负责 `free`\n- **结构体数组 / 嵌套 / 位域**：`stus[i]` ≡ `*(stus + i)`；嵌套成员逐层用 `.` 访问如 `stu1.birth.year`；位域 `类型 成员名:位数` 共用内存，嵌入式寄存器操作常用（如 `id:10 + age:7 + sex:1 + money:14 = 32` 位）\n\n#### 共用体、枚举与 I/O（Day21）\n\n**共用体（union）**：所有成员**共用同一块内存**，总大小 = 宽度最大成员的大小；访问**最近一次被赋值**的成员才有意义。\n\n```c\nd1.l = 0x67666564636261;   // 小端下内存低地址→高地址：61 62 63 64 65 66 67\nprintf(\"%s\\n\", d1.cs);     // 输出 \"abcdefg\"（依赖小端字节序）\n```\n\n- 应用：不同角色不同考核标准（学生看分数/老师看等级/招生看人数）、拆分 `float` 的 4 个字节\n\n**枚举（enum）**：本质就是 `int`，只是取值有限且给每个值起了有意义的名字，可读性比直接用 `int` 好。\n\n```c\nenum xueli { chuzhong, gaozhong, benke = 5, dazhuan = 8, yanjiusheng };\n// 取值：0, 1, 5, 8, 9（默认从 0 开始每项 +1，可显式赋值，其后在此基础上继续 +1）\n// 不同枚举类型之间元素不能同名\n```\n\n- **I/O 三种**：标准 I/O、文件 I/O、串 I/O\n- **标准 I/O**：`stdin`/`stdout` 行缓冲，`stderr` 无缓冲（专门输出异常/错误）；三种缓冲机制：无缓冲/行缓冲/全缓冲；进程正常结束自动刷新所有 I/O 缓冲区；`scanf` 只从标准输入缓冲区提取期望类型的数据，返回值为成功提取的个数，失败返回 0，缓冲区空则等待\n- **文件 I/O**：三步曲 `fopen` → `fread`/`fwrite` 等 → `fclose`；打开模式 `w`（不存在则创建、存在则清空）、`r`（不存在或不可读则失败返回 NULL）、`a`（追加，写入指针初始在文件末尾）；另有 `r+/w+/a+` 和 `b` 二进制模式；文本文件（纯字符/字符串）vs 二进制文件（含 int/float/结构体等，文本编辑器打开显示乱码）\n- **串 I/O**：`sscanf`（字符串 → 其他类型）、`sprintf`（其他类型 → 字符串）\n- **三个标准文件指针**：`stdin`/`stdout`/`stderr` 系统自动打开；`printf` ≡ `fprintf(stdout,...)` ≡ `fputs(...,stdout)`；`getchar` ≡ `scanf(\"%c\",&c)` ≡ `fscanf(stdin,...)` ≡ `fgetc(stdin)` ≡ `fread(&c,1,1,stdin)`\n\n#### 今日笔记整理\n\n将 Day19/20/21 三篇笔记整理重排，修正的笔误包括：Day20 两处 `char name[31]` 漏分号；Day21 `fputs` 参数顺序写反、`sex_t s1 = v` 笔误。\n\n### Rust\n\n无。\n\n## 思考\n\n无。\n\n## 明日计划\n\n1. C 语言：完成今日作业（strncpy / strcat / 字符串转 int / 图书记录系统增删改查 / 判断字节序）\n2. C 语言：文件 I/O 深入（fread/fwrite 二进制读写）\n3. Rust：恢复学习节奏",
            "url": "https://kyriechao.github.io/posts/2026/08/2/24-diary",
            "title": "2026-08-24 周一 · 结构体、共用体、枚举与 I/O",
            "summary": "C 语言：结构体深入（声明/初始化/内存对齐/嵌套/位域）+ 共用体（union）+ 枚举（enum）+ I/O（标准/文件/串），今日整理 Day19/20/21 三篇笔记",
            "date_modified": "2026-08-24T13:37:26.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/2/20-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n学习内容：[C 语言 Day19 笔记](../嵌入式/C语言/day19.md)——**空类型指针、内存管理与结构体**\n\n#### 空类型指针（void*）\n\n`void*` 表示**不确定基类型的指针**，不能直接解引用。它可以代表一切其他类型的指针，其他指针与 `void*` 之间可自动转型（建议 `void*` 转具体类型时用**强制转型**，因为 C++ 编译器不支持自动转）。\n\n> \"一切皆空，但空不是一切\"——`void*` 能承接任意指针，但它自身不知道指向什么类型。C 语言通过 `void*` 实现**泛型编程（GP, General Programming）**：与具体数据类型无关的编程处理方式。\n\n#### 内存管理深入\n\n五区详细对比：\n\n| 区域                 | 容量                     | 管理方式 | 初始化   | 存放内容                |\n| -------------------- | ------------------------ | -------- | -------- | ----------------------- |\n| 栈区（Stack）        | 很小（若干兆，易栈溢出） | 系统自动 | 不初始化 | 形参、非静态局部变量    |\n| 堆区（Heap）         | 很大                     | 程序亲自 | 不初始化 | 动态分配（`malloc` 等） |\n| 静态存储区（Static） | 很大                     | 系统自动 | 自动清零 | 静态局部变量、全局变量  |\n| 常量区（Constant）   | —                        | 系统自动 | —        | 常量（只读）            |\n| 代码区（Code）       | —                        | —        | —        | 指令（函数体）          |\n\n> **const 的存储位置**：用 `const` 修饰的**非静态局部变量**只是\"名义上的常量\"（不能通过名字写），仍存在**栈区**；而 `const` 修饰的**静态局部变量**和**全局变量**才是真正常量，存在**常量区**。\n\n**堆区内存管理库函数**（`stdlib.h`）：`malloc`、`calloc`、`realloc`、`free`。另积累：`perror`（打印错误信息）、`exit`（结束程序，参数即返回值）。\n\n| 对比   | malloc         | calloc                      |\n| ------ | -------------- | --------------------------- |\n| 参数   | 1 个（字节数） | 2 个（元素个数 + 元素宽度） |\n| 初始化 | 不清零         | 自动清零                    |\n| 效率   | 更高           | 稍低                        |\n| 适用   | 一般场景       | 更适合堆区数组              |\n\n`realloc` 调整堆区空间大小：扩大时若后面空间充足就**就地扩大**（返回值 = 原指针），否则**申请更大的新空间**、拷贝旧数据、释放旧空间（返回值 ≠ 原指针）。\n\n**内存操作库函数**：`memset`（逐字节设值）、`memcpy`（内存拷贝，不处理重叠）、`memmove`（内存拷贝，处理重叠）、`memcmp`（内存比较，全同返回 0）。\n\n> **经典面试题**：`memcpy` 和 `memmove` 有啥区别？如何选择？——`memcpy` 假定两块内存不重叠、效率更高；`memmove` 能正确处理重叠区域（相当于\"安全版\"拷贝）。不确定是否重叠时用 `memmove`。\n\n#### 结构体（Struct）\n\n**结构体**：若干个不同类型（也可相同类型）数据构成的集合。构成结构体的数据称为**数据成员（Data Member）**，也叫属性或字段（Field）。用于表示一件商品、一个学生、一本书等信息。\n\n结构体是**自定义数据类型**，必须先声明类型、再定义变量。声明语法：\n\n```c\nstruct book\n{\n    int isbn;\n    char name[31];\n    float price;\n    short type;\n    int cnt;\n};\n```\n\n#### 双向链表练习\n\n今日自己练习了**双向链表（doubly linked list）**：每个节点除 `next` 指向后继外，还多一个 `prev` 指向前驱。\n\n- 节点结构：`struct Node { int val; struct Node *prev; struct Node *next; };`\n- 优点：可**双向遍历**；删除节点时无需像单向链表那样额外保存前驱指针；插入更灵活\n- 代价：每节点多存一个指针，插入/删除要同时维护 `prev`、`next` 两个指针\n\n### Rust\n\n无。\n\n## 思考\n\n无。\n\n## 明日计划\n\n1. C 语言：结构体深入（结构体指针、结构体数组、typedef 结构体）、链表进阶\n2. Rust：恢复学习节奏",
            "url": "https://kyriechao.github.io/posts/2026/08/2/20-diary",
            "title": "2026-08-20 结构体与动态内存管理",
            "summary": "C 语言空类型指针（void*）+ 内存管理深入（malloc/calloc/realloc/free、内存操作函数）+ 结构体入门 + 双向链表练习，Rust 今日休息",
            "date_modified": "2026-08-20T13:54:04.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/19-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n学习内容：[C 语言 Day18 笔记](../嵌入式/C语言/day18.md)——**二级指针与内存管理**\n\n#### 二级指针和多级指针\n\n**二级指针（double pointer）**：表示一个指针类型数据的内存地址（指向指针的指针）。**三级指针**表示二级指针的地址，以此类推，统称**多级指针**。\n\n```c\nshort s = 0x1234;\n\nshort* p1  = &s;     // 一级指针，指向 short\nshort** p2 = &p1;    // 二级指针，指向 short*\nshort*** p3 = &p2;   // 三级指针，指向 short**\nshort**** p4 = &p3;  // 四级指针，指向 short***\n\n**p2 += 5;   // 解引用两次回到 s，等价于 s += 5;\n--***p3;     // 解引用三次回到 s，等价于 --s;\n```\n\n> 解引用层级：`*p2` 得到 `p1`（一级指针），`**p2` 才得到最终的 `s`。多级指针就是\"指针的指针的指针…\"，每多一级就多一层间接寻址。\n\n#### 内存管理（Memory Management）\n\n操作系统给每个进程分配一块**独立的私有地址空间**，又分为几个区域：\n\n| 区域                 | 存放内容                             |\n| -------------------- | ------------------------------------ |\n| 栈区（stack）        | 局部变量、函数调用（形参、返回地址） |\n| 堆区（heap）         | 动态分配的内存（`malloc`/`free`）    |\n| 静态存储区（static） | 全局变量、静态变量                   |\n| 常量区（constant）   | 字符串字面量、常量                   |\n| 代码区（text）       | 程序的机器指令（函数体）             |\n\n> 之前学过的知识点在这里串起来了：局部变量在栈区（函数结束即释放，所以不能返回其指针）；字符串字面量在常量区（只读，修改会段错误）；函数在代码区（所以有函数指针）；`malloc` 在堆区（手动申请/释放）。\n\n#### 链表练习（test.c）\n\n今日继续练习链表，`test.c` 里对比了两套实现：\n\n- **link.c / link.h**：单向链表**简洁版**，函数名不带前缀——`create` / `insert_head` / `insert_tail` / `show` / `find` / `reverse` / `delete` / `destroy`\n- **list.c / list.h**：单向链表 + 循环链表**完整版**（`link_xxx` 与 `clink_xxx` 两套）\n- **main.c**：测试用例（创建 → 尾插 → 头插 → 反转 → 删除 → 查找 → 销毁）\n\n核心操作全部覆盖：头插、尾插、打印、反转、查找、删除、销毁；循环链表版用 `head->next == head` 判断遍历终点。\n\n### Rust\n\n无。\n\n## 思考\n\n无。\n\n## 明日计划\n\n1. C 语言：结构体（struct）、动态内存分配深入（malloc/free）、字符串库函数\n2. Rust：恢复学习节奏\n",
            "url": "https://kyriechao.github.io/posts/2026/08/1/19-diary",
            "title": "2026-08-19 周三 · 二级指针与内存管理",
            "summary": "C 语言二级指针/多级指针 + 内存管理（栈/堆/静态/常量/代码区）+ 链表练习（单向链表与循环链表），Rust 今日休息",
            "date_modified": "2026-08-19T14:14:31.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/18-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n学习内容：**typedef 与函数指针**\n\n#### typedef 关键字\n\n`typedef`（type definition，类型定义）给已有类型起**别名**，意义是让类型名更有意义、更简短，提升可读性。语法 `typedef 原类型 新类型名;`，实际项目中类型声明通常写在头文件。\n\n- 一行可声明多个别名：`typedef unsigned long price, *money;` 中 `price` 是 `unsigned long`，`money` 是 `unsigned long*`\n- 常配合指针类型使用：`typedef unsigned long* data;`\n\n#### 函数与指针\n\n**返回值可以是指针类型**，但**不能返回指向非静态局部变量的指针**——函数返回后该变量已释放，会得到**悬空指针（dangling pointer）**。要返回就返回仍存活的对象：静态局部变量、全局变量、实参地址、`malloc` 内存。\n\n**形参可以是指针类型**：传大数组/结构体时只传地址，效率高。君哥建议——只读不写时把形参定义为**常量指针**（`const`），误写会被编译器直接拦下。\n\n**函数指针（function pointer）**：函数也占内存（代码段），其首字节地址就是入口地址。函数名本身就是函数指针。\n\n- 定义：`double(*pf)(double, double)`；`(*pf)` 括号不能省，否则变成\"返回 `double*` 的函数\"\n- 调用：`pf2(8, 3)` 与 `max(8, 3)` 等效\n- 类型声明后更简洁：`typedef double(*fun_t)(double, double);`，可定义变量/常量/数组\n- 函数指针数组：`fun_t fs[] = {sum, max, sub};`，`fs[1](5, 6)` 即 `max(5, 6)`\n\n#### 字符型指针（char*）\n\n两个用途：① 表示某字符的地址；② 表示一个字符串（用首字符指针代表，字符连续存储、末尾 `'\\0'`）。\n\n- `char s1[] = \"ab12\"` 是数组（数组名是常量），不能 `s1 = \"...\"`；`char* s2 = \"ab12\"` 是指针变量，可以改指向\n- **字符串字面量**存在常量区，用 `char*` 指向后**不应修改**其内容，否则是未定义行为\n- **段错误（Segmentation fault）**：内存非法访问（访问已释放内存、改只读区、野指针解引用）\n\n#### 链表练习（test2.c）\n\n今日练习用结构体和 `malloc`/`free` 实现了两类链表，并验证各操作：\n\n| 操作        | 单向链表（不循环）            | 循环链表（头结点自环）          |\n| ----------- | ----------------------------- | ------------------------------- |\n| 创建        | `link_create`（`next=NULL`）  | `clink_create`（`next=head`）   |\n| 头插 / 尾插 | `insert_head` / `insert_tail` | `cinsert_head` / `cinsert_tail` |\n| 打印        | `print_link`（到 NULL）       | `cprint_link`（回到 head）      |\n| 反转        | `link_reverse`                | `creverse_link`                 |\n| 查找        | `link_find`                   | `cfind_link`                    |\n| 删除        | `link_delete`                 | `cdelete_link`                  |\n| 销毁        | `link_destroy`                | `cdestroy_link`                 |\n\n### Rust\n\n无。\n\n## 思考\n\n### 爱与欲：两个状态的冲突\n\n今天思考了\"爱而无欲，欲而无爱\"这个命题。\n\n我的理解不是普通意义上的\"爱情包含爱和欲两个部分\"，而是：\n\n> 爱是一种状态，欲也是一种状态。\n\n两者不是程度不同，而是本质不同。\n\n#### 1. 爱是一种纯粹的状态\n\n我理解的爱，不是\"我想得到你\"，而是：\n\n> 我关注的是你这个人的存在。\n\n真正爱一个人的时候，不应该带有目的性。如果我爱一个人，却希望她幸福，即使最后陪伴她的人不是我，那这种爱似乎更接近爱的本质——因为这里没有占有、没有索取。爱的时候，就只是爱，类似进入一种心流状态：不计算得失、不考虑回报、不分析价值、不想着自己正在爱，只是单纯地存在于这种状态里。\n\n#### 2. 欲是一种环境和基因的表达\n\n欲望更像是：\n\n> 基因 + 环境 + 经验共同产生的反应。\n\n它来自人的生物本能，关注的是\"我想获得什么\"——可能是身体吸引、陪伴、关注、占有、满足自己的需求。因此欲望本身并不等于爱，因为欲望的方向更多是自己。\n\n#### 3. 真爱是否存在？\n\n我的观点：真正完全纯粹、不带任何欲望、不受环境和本能影响的爱，也许不存在。现实中的人是复杂的，感情受生理机制、社会环境、个人经历、过去记忆影响。所以\"永恒的真爱\"可能是一种理想状态。\n\n但这不代表爱不存在，可能只是：\n\n> 永恒纯粹的爱不存在，而某一刻真实发生的爱存在。\n\n就像心流无法永久保持，但心流状态本身是真实存在的。\n\n#### 4. 我的矛盾：为什么无法想象爱和欲同时面对一个人？\n\n我发现自己无法想象两个场景：用纯粹的爱面对喜欢的人，和带着欲望面对喜欢的人。我感觉无法同时对一个人表达这两个方面。\n\n因为在理解里，爱意味着\"你是一个独立的人\"，欲望意味着\"我想从你那里获得某些东西\"。爱更接近\"你是你\"，欲更接近\"我想要你\"。\n\n所以冲突产生了：如果我真的爱一个人，我怎么还能对她产生欲望？如果产生欲望，我是不是已经不再爱她，而只是在满足自己？\n\n#### 5. 进一步的问题\n\n可能真正的问题不是\"有没有欲望\"，而是：\n\n> 当欲望出现的时候，我是否仍然把对方当作一个完整的人。\n\n如果欲望意味着占有、把对方当工具，那它和爱冲突；但如果欲望只是想靠近、想连接、想表达亲密，那它可能不一定破坏爱。\n\n#### 6. 最后的疑问\n\n我似乎不是在寻找普通意义上的爱情，而是寻找一种：\n\n> 不欺骗自己、不把欲望包装成爱、不把占有误认为关心的关系。\n\n但现实中的关系可能无法达到绝对纯粹。所以矛盾是：如果绝对纯粹的真爱可能不存在，那为什么还要进入一段关系？\n\n这次思考的核心：**爱是否必须纯粹，才能称为爱？** 或者：**真正成熟的爱，不是消灭欲望，而是在拥有欲望的时候，依然尊重对方作为一个人的存在。**\n\n## 明日计划\n\n1. C 语言：结构体（struct）、动态内存分配（malloc/free）、链表进阶\n2. Rust：恢复学习节奏",
            "url": "https://kyriechao.github.io/posts/2026/08/1/18-diary",
            "title": "2026-08-18 周二 · typedef 与函数指针",
            "summary": "C 语言 typedef 与函数指针（返回值/形参/函数指针/字符指针）+ 链表练习（单向链表与循环链表），Rust 今日休息",
            "date_modified": "2026-08-18T11:09:15.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/17-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n学习内容：**指针（进阶）**\n\n#### 指针常量与常量指针\n\n三者区别核心在\"谁是不能被改的\"：\n\n| 类型                                         | 定义                        |       指针本身       |       指向的数据       |\n| -------------------------------------------- | --------------------------- | :------------------: | :--------------------: |\n| 指针常量（constant pointer）                 | `int* const p2 = &a;`       |  常量（不可改指向）  |  变量（可 `*p2 = 5`）  |\n| 常量指针（pointer to constant）              | `const int* p3 = &a;`       | 变量（可 `p3 = &b`） | 常量（不可 `*p3 = 5`） |\n| 常量指针常量（constant pointer to constant） | `const int* const p4 = &a;` |         常量         |          常量          |\n\n> 区分秘诀：从左向右读，碰到 `const` 读\"常量\"，碰到 `*` 读\"指针\"。`const int* p` 与 `int const* p` 完全等效。\n\n#### 指针的相关运算\n\n- **指针 ± 整数**：按**基类型宽度**偏移（不是按字节）。`p1 + 1` 偏移一个 `int` 的宽度。\n- **同类型指针相减**：结果为距离，单位是基类型宽度（元素个数），不是字节数。\n- **指针强制类型转换**：转换基类型，指针的**值不变**，但解引用时对内存的解释方式变了。如 `int i = 0x364241;`，用 `char*` 解引用读到 `'A'`（0x41），用 `short*` 读到 16961。\n\n#### 数组与指针\n\n- **指针数组**：元素是指针的数组，本质是**数组**。`int* ps[5]`，每个元素是 `int*`。\n- **数组指针**：指向数组的指针，本质是**指针**。`int (*pa)[5]`，指向整个 `int[5]` 数组，`(*pa)[3] = 9` 即 `nums[3] = 9`。\n- **数组名 = 首元素指针**：`nums` 等效于 `&nums[0]`，与 `&nums` 值相同但基类型不同（前者指向元素，后者指向整个数组）。\n- **下标运算本质是指针运算**：`a[n]` 完全等效于 `*(a+n)`。\n\n> 记忆技巧：`[]` 优先级高于 `*`，所以 `int* ps[5]` 先结合 `[]` 是数组；`int (*pa)[5]` 用括号让 `*` 先结合，是指针。\n\n### Rust\n\n无。\n\n## 思考\n\n无。\n\n## 明日计划\n\n1. C 语言：指针与函数（指针作参数/返回值）、函数指针、二级指针",
            "url": "https://kyriechao.github.io/posts/2026/08/1/17-diary",
            "title": "2026-08-17 周一 · 指针（进阶）",
            "summary": "C 语言指针进阶：指针常量/常量指针/常量指针常量 + 指针运算（加减/相减/强转）+ 数组与指针（指针数组/数组指针/下标运算本质）",
            "date_modified": "2026-08-17T11:50:53.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/14-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n学习内容：[C 语言 Day15 笔记](../嵌入式/C语言/day15.md)——**字符数组进阶 + 指针入门**\n\n#### 中文字符的编码表示\n\n`char` 只有 1 字节，只能表示 ASCII；中文需要多字节编码。\n\n| 编码方案                 | 归属     | 中文占用                    |\n| ------------------------ | -------- | --------------------------- |\n| GB2312 / GBK / GB18030   | 中国国标 | 2 字节（GB18030 可 4 字节） |\n| Unicode / UTF-8 / UTF-16 | 国际标准 | UTF-8 中文一般 3 字节       |\n\n- 中文版 Windows 默认 **GBK**，Linux/macOS 默认 **UTF-8**\n- **乱码根本原因**：编码和解码用的字符集不一致\n- **换行符差异**：Linux/macOS 用 `\\n`（LF），Windows 用 `\\r\\n`（CRLF）\n\n#### 三维数组与二维字符数组\n\n- **三维数组**：二维数组构成的数组，形象理解为\"一栋楼\"（若干层，每层是行列房间）。`double nums1[3][4][5]`\n- **通用规律**：给了初值后，只能省略**第一维**的长度，其余维必须写，编译器自动推导第一维\n- **二维字符数组 = 字符串数组**：`char all_names[50][31]`，`all_names[i]` 是第 i 个字符串；重新赋值要用 `strcpy` 不能用 `=`\n\n#### 数组与函数（再强调）\n\nC 语言不希望数组作返回值/形参，原因是**内存拷贝开销**。数组作形参时编译器**退化**为指针：`void f(short data[100])` / `void f(short data[])` / `void f(short* data)` 三种写法完全等价，`sizeof(data)` 在 64 位下恒为 8。\n\n#### 指针入门\n\n**指针是一种数据类型，用来表示内存地址**，本质是无符号整数（32 位 4 字节、64 位 8 字节）。操作系统把内存按字节编号，编号即地址，CPU 通过地址读写数据。\n\n| 运算符 | 名称         | 作用                                   |\n| ------ | ------------ | -------------------------------------- |\n| `&`    | 取地址运算符 | 单目，得到操作数的内存地址             |\n| `*`    | 解引用运算符 | 单目，操作数是地址，得到该地址处的数据 |\n\n> 两个运算符都是**单目**，与二目的按位与 `&`、乘法 `*` 区分开。指针占位符是 `%p`。\n\n几个关键点：\n\n- **地址用首字节地址表示**：数据类型决定宽度，首字节地址 + 宽度即可唯一确定数据\n- **大小端存储**：小端（Little Endian）低位字节存低地址，大端相反；X86/ARM 等主流平台都是**小端**\n- **定义指针**：`基类型* 变量名;`，基类型 = 指针所指向的数据类型；`*p1 = 5` 与 `j = 5` 等价（通过指针/变量名访问内存）\n- **NULL 指针**：值为 0，可赋给任意指针，表示\"不指向任何有效内存\"；暂不确定指向时先初始化为 `NULL` 是好习惯\n\n#### 附录：排序算法选型速查表\n\n承接 Day14 排序预览，嵌入式场景选型三梯队：\n\n| 梯队     | 算法                     | 复杂度     | 定位                                               |\n| -------- | ------------------------ | ---------- | -------------------------------------------------- |\n| 第一梯队 | 计数排序                 | O(n)       | 数据范围极狭小时无敌（如 ADC 0~4095、温度 -40~85） |\n| 第二梯队 | 快排/qsort、归并、堆排序 | O(n log n) | 通用乱序数据主力                                   |\n| 第三梯队 | 插入、选择、冒泡         | O(n²)      | 小数据或特定硬件                                   |\n\n> 选型口诀：**日常业务数据用 `qsort`，排 ADC 值用计数排序，做任务调度用堆排序，排几个零散变量用插入排序。** 嵌入式项目建议直接用系统库 `qsort`，鲁棒性远高于手写。\n\n### Rust\n\n今天没学习，休息。\n\n## 思考\n\n无。\n\n## 明日计划\n\n1. C 语言：指针深入（指针运算、指针与数组、指针与函数）\n2. Rust：恢复学习节奏",
            "url": "https://kyriechao.github.io/posts/2026/08/1/14-diary",
            "title": "2026-08-14 周五 · 字符数组进阶与指针入门",
            "summary": "C 语言字符数组进阶（中文编码/三维数组/字符串数组）+ 指针入门（内存地址/&与*/大小端/NULL），Rust 今日休息",
            "date_modified": "2026-08-14T13:33:17.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/13-diary",
            "content_html": "## 技术\n\n### C 语言\n\n学习内容：[C 语言 Day13 笔记](../嵌入式/C语言/day13.md)——**数组（Array）**\n\n#### 数组概述\n\n数组是**相同类型**数据的**有序集合**，用于批量数据处理。核心概念：\n\n| 概念            | 说明                   | 类比                     |\n| --------------- | ---------------------- | ------------------------ |\n| 元素（Element） | 数组里的每个数据       | 楼里的每间房             |\n| 下标（Index）   | 从 **0** 开始的编号    | 房间号（第 1 间是 0 号） |\n| 长度（Length）  | 元素总个数             | 总房间数                 |\n| 数组名          | 首元素地址（指针常量） | 楼的地址                 |\n\n> 下标从 0 开始是 C 设计者有意为之：`arr[i]` 底层是 `*(arr + i)`，i=0 就是首元素本身。\n\n#### 定义数组的四种语法\n\n| 方式           | 写法                      | 特点                              |\n| -------------- | ------------------------- | --------------------------------- |\n| 只定义不初始化 | `int a[5];`               | 局部数组垃圾值，全局/静态自动清零 |\n| 完全初始化     | `int b[5] = {1,2,3,4,5};` | 全部指定                          |\n| 部分初始化     | `int c[5] = {1,2};`       | 后面自动清零 → `{1,2,0,0,0}`      |\n| 省略长度       | `int d[] = {1,2,3};`      | 编译器推算长度 = 3                |\n\n初始化技巧：\n\n- 全零：`int arr[100] = {0};`（最常用）或 `memset(arr, 0, sizeof(arr))`\n- 常量数组：`const int nums[5] = {...};` 必须初始化，之后不可改\n- 用宏代替魔数：`#define SIZE 10`\n\n#### 访问与遍历\n\n数组不能整体访问，只能通过**下标运算符 `[]`** 访问单个元素，`arr[i]` 用法和普通变量完全一样。\n\n三种遍历模式：正序 for、用 `sizeof(arr)/sizeof(arr[0])` 动态算长度（推荐）、反向遍历。\n\n**数组内存 = 长度 × 类型宽度**，如 `int a[10]` 占 10×4 = 40 字节。\n\n#### 数组越界\n\n访问下标超出 `0 ~ 长度-1` 范围即越界。**C 语言不检查越界**（不像 Java/Python 抛异常）——为极致性能付出的安全代价，是 C 语言排名第一的隐形杀手。\n\n| 情形     | 后果                                  |\n| -------- | ------------------------------------- |\n| 轻微越界 | 踩到相邻变量，数据莫名其妙被改        |\n| 严重越界 | 踩到返回地址，Segmentation Fault 崩溃 |\n| 最隐蔽   | 看似正常，换个环境/数据就崩           |\n\n防御：循环条件用宏常量、`assert(index >= 0 && index < SIZE)`、手动检查。\n\n#### 数组与函数——退化指针\n\n数组作函数参数时**长度信息丢失**，三种写法等价：`int arr[]` / `int arr[10]` / `int *arr`，本质都是指针。必须额外传 `len` 参数。\n\n> **sizeof 陷阱：** 函数内部 `sizeof(arr)` 只能得到指针大小（4 或 8），不是数组实际字节数。sizeof 只有在定义数组的同一作用域内才有效。\n\n#### 多维数组、字符数组与 VLA\n\n- **二维数组**：`int matrix[3][4]`，逻辑上矩阵，内存中**行优先**连续一维存储\n- **字符数组**：C 语言无 String 类型，用 `\\0`（空字符）结尾的 char 数组表示字符串。`char s[] = \"Hello\"` 自动加 `\\0`，长度 = 6。标准库字符串函数都依赖 `\\0`\n- **C99 VLA**：`int arr[n]` 运行时定长，C11 变为可选、MSVC 从不支持，**嵌入式通常禁用**（栈空间不可预知），用 `malloc` 更安全\n\n### Rust\n\n今日起放慢 Rust 学习节奏（\"慢点学习\"），以消化、复习已学内容为主，不赶进度。\n\n## 思考\n\n无。\n\n## 明日计划\n\n1. C 语言：进入数组常用操作（查找、插入、删除、排序、逆序）\n2. Rust：保持慢节奏，复习阶段 3（集合、迭代器、Todo CLI）",
            "url": "https://kyriechao.github.io/posts/2026/08/1/13-diary",
            "title": "2026-08-13",
            "summary": " 技术\n\n C 语言\n\n学习内容：[C 语言 Day13 笔记](../嵌入式/C语言/day13.md)——数组（Array）\n\n 数组概述\n\n数组是相同类型数据的有序集合，用于批量数据处理。核心概念：\n\n| 概念            | 说明     ...",
            "date_modified": "2026-08-13T12:36:13.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/13-rust-day-14",
            "content_html": "\n# Rust Day14 — 学习节奏调整\n\n---\n\n## 一、今日说明\n\n今天（2026-08-13）没有学习 Rust 新内容，休息一天。\n\n> 前三天节奏偏快：Day11 一口气完成了「格式化宏 + 阶段 0 热身 + 阶段 1 所有权与借用」，Day12 完成「阶段 2」，Day13 完成「阶段 3」。连续高强度推进，内容消化得不够扎实，需要停下来缓一缓。\n\n## 二、节奏调整：从明天起放慢\n\n从明天（2026-08-14，即 Day15）开始，学习节奏放慢，主要调整如下：\n\n| 维度       | 调整前           | 调整后                          |\n| ---------- | ---------------- | ------------------------------- |\n| 每日内容量 | 一天一个阶段     | 一个阶段拆成 2~3 天             |\n| 练习方式   | 边学边写、赶进度 | 学一块 + 独立练一块，吃透再往下 |\n| 复习安排   | 无固定安排       | 每天开头先回顾前一天内容        |\n\n> 放慢不是停滞，而是把「学完」变成「学会」。Rust 的所有权、trait、生命周期这些核心概念，慢下来反而更快。\n\n## 三、阶段 4 拆分计划（可调整）\n\n原计划「阶段 4：泛型 / trait / lifetime 综合」一天完成，现拆分为：\n\n| 天    | 内容                   | 说明                                      |\n| ----- | ---------------------- | ----------------------------------------- |\n| Day15 | 泛型（Generic）基础    | 泛型函数、泛型结构体、单态化              |\n| Day16 | Trait 与 Trait Bound   | 定义 trait、实现 trait、`impl Trait` 返回 |\n| Day17 | 生命周期（Lifetime）   | 生命周期标注、借用检查器                  |\n| Day18 | 阶段 4 综合练习 + 收尾 | 三个概念串起来的小项目                    |\n\n## 四、小结\n\n| 事项     | 状态            |\n| -------- | --------------- |\n| 今日学习 | 休息，未学      |\n| 节奏调整 | 从 Day15 起放慢 |\n| 阶段 4   | 拆 4 天推进     |\n\n**明天：** 慢节奏进入阶段 4，从泛型开始。",
            "url": "https://kyriechao.github.io/posts/2026/08/1/13-rust-day-14",
            "title": "Rust 学习计划",
            "summary": "\n Rust Day14 — 学习节奏调整\n\n---\n\n 一、今日说明\n\n今天（2026-08-13）没有学习 Rust 新内容，休息一天。\n\n> 前三天节奏偏快：Day11 一口气完成了「格式化宏 + 阶段 0 热身 + 阶段 1 所有权与借用」，Day12 完成「阶段 2」，Day13 完...",
            "date_modified": "2026-08-13T12:36:13.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/12-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n学习内容：**数组（Array）**\n\n#### 核心概念\n\n数组是相同类型数据的**有序集合**，用于批量处理。下标从 **0** 开始（因为 `arr[i]` 底层是 `*(arr + i)`）。\n\n#### 定义数组的四种语法\n\n| 方式           | 写法                      | 特点                         |\n| -------------- | ------------------------- | ---------------------------- |\n| 只定义不初始化 | `int a[5];`               | 局部数组垃圾值               |\n| 完全初始化     | `int b[5] = {1,2,3,4,5};` | 全部指定                     |\n| 部分初始化     | `int c[5] = {1,2};`       | 后面自动清零 → `{1,2,0,0,0}` |\n| 省略长度       | `int d[] = {1,2,3};`      | 编译器自动推算长度=3         |\n\n初始化技巧：\n\n- 全零：`int arr[100] = {0};`\n- 算长度：`sizeof(arr) / sizeof(arr[0])`\n- 常量数组：`const int nums[5] = {...};`\n\n#### 遍历与越界\n\n三种遍历模式：正序 for、用 sizeof 动态算长度、反向遍历。\n\n**数组越界是 C 语言排名第一的隐形杀手。** C 语言不检查越界——为了性能付出安全代价。轻微越界踩坏相邻变量，严重越界直接 Segmentation Fault，最隐蔽的是\"看似正常\"。防御：宏常量、`assert`、手动检查。\n\n#### 数组与函数——退化指针\n\n数组作函数参数时**长度信息丢失**，三种写法等价：`int arr[]` / `int arr[10]` / `int *arr`。必须额外传 `len` 参数。\n\n> `sizeof` 陷阱：函数内部 `sizeof(arr)` 只能得到指针大小（4 或 8），**不是**数组实际字节数。\n\n#### 其他\n\n- **二维数组**：行优先存储，逻辑上矩阵，内存中连续一排\n- **字符数组**：C 语言无 String 类型，用 `\\0` 结尾的 char 数组表示字符串。`char s[] = \"Hello\"` 自动加 `\\0`，长度=6\n- **C99 VLA**：`int arr[n]`，C11 可选，MSVC 不支持，嵌入式通常禁用\n\n### Rust\n\n学习内容：[Rust Day13 笔记](12-rust-day-13.md)——**阶段 3：集合、迭代器与 Todo CLI**\n\n#### MyStack（封装 Vec）\n\n用 `Vec<i32>` 封装自定义栈：`push` / `pop` / `peek` / `is_empty`。`peek()` 返回 `Option<&i32>`——只借看一眼，不拿走值。\n\n#### HashMap 核心：entry API\n\n```rust\n*hash.entry(c).or_insert(0) += 1;  // 一次查+插，返回 &mut i32\n```\n\n这个模式替代了\"先 get 判空，再 insert\"的两步操作。练习了字符频率统计、单词频率统计（key 用 `&str` 避免多分配）、`max_by_key` 找最大 value 的 key。\n\n#### HashSet 交集\n\n从 O(n²) 双层 for 优化到 O(n+m)：`set_b.contains(x)` 是 O(1) 的。\n\n#### 迭代器组合技\n\n| 操作                     | 功能                             |\n| ------------------------ | -------------------------------- |\n| `filter + map + collect` | 标准链式转换                     |\n| `flatten`                | `Vec<Vec<T>>` 拍平成 `Vec<T>`    |\n| `sort` + `dedup`         | 先排序再删相邻重复（顺序不能反） |\n| `partition`              | 根据谓词一次遍历分成两个 Vec     |\n\n#### 文件排序 + Todo CLI\n\n`sort_lines_in_file`：读→`lines()`→`sort()`→`join`→写回。关键：`map(String::from)` 把借用的 `&str` 转为拥有的 `String`，避免悬垂引用。\n\nTodo CLI 完整项目——`Task` struct + serde JSON 持久化 + 四命令（add/list/done/clear），架构：**parse_args → load → 业务 → save**。\n\n#### 本日收获\n\n- `HashMap::entry().or_insert()` 是最常用的 HashMap 模式\n- `&str` 做 HashMap key 可以从原字符串借用，零分配\n- `Box<dyn Error>` 适合作顶层函数返回类型，灵活\n- CLI 项目的基本架构模式：命令行解析 → 加载状态 → 执行操作 → 保存状态\n\n## 明日计划\n\n1. C 语言：跟课堂节奏，可能进入数组进阶或指针章节\n2. Rust：继续阶段 3，Maybe 进入阶段 4（泛型/trait/lifetime）",
            "url": "https://kyriechao.github.io/posts/2026/08/1/12-diary",
            "title": "2026-08-12 周二 · 数组起步与阶段 3",
            "summary": "C 语言数组（定义/遍历/越界/退化指针/多维/字符数组），Rust 阶段 3 集合、迭代器与 Todo CLI",
            "date_modified": "2026-08-12T13:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/12-rust-day-13",
            "content_html": "\n# Rust Day13 — 阶段 3：集合、迭代器与 Todo CLI\n\n---\n\n## 一、自定义栈 — `MyStack`（封装 Vec）\n\n```rust\nstruct MyStack {\n    vec: Vec<i32>,\n}\n\nimpl MyStack {\n    fn new() -> Self {\n        MyStack { vec: Vec::new() }\n    }\n    fn push(&mut self, val: i32) {\n        self.vec.push(val);\n    }\n    fn pop(&mut self) -> Option<i32> {\n        self.vec.pop()\n    }\n    fn peek(&self) -> Option<&i32> {\n        self.vec.last()    // last() 返回 Option<&T>，不移动所有权\n    }\n    fn is_empty(&self) -> bool {\n        self.vec.is_empty()\n    }\n}\n```\n\n| 方法       | 返回类型       | 说明                                  |\n| ---------- | -------------- | ------------------------------------- |\n| `push`     | `()`           | 入栈，直接委托 `Vec::push`            |\n| `pop`      | `Option<i32>`  | 出栈，`Vec::pop` 本身就是 `Option<T>` |\n| `peek`     | `Option<&i32>` | 看一眼栈顶，不拿走，`Vec::last()`     |\n| `is_empty` | `bool`         | 直接用 `Vec::is_empty()`              |\n\n> `peek()` 返回 `Option<&i32>` 而不是 `Option<i32>`——只是借看一眼，不拿走值。如果用 `pop()` 就拿走了。\n\n---\n\n## 二、HashMap — entry API\n\n### 2.1 字符频率统计\n\n```rust\nuse std::collections::HashMap;\n\nfn char_frequency(s: &str) -> HashMap<char, i32> {\n    let mut hash = HashMap::new();\n    for c in s.chars() {\n        *hash.entry(c).or_insert(0) += 1;\n    }\n    hash\n}\n```\n\n**entry API 拆解：**\n\n```rust\nhash.entry(c)       // 找到键 c 所在的槽位\n    .or_insert(0)   // 如果不存在 → 插入 0 并返回 &mut i32；如果存在 → 返回 &mut i32\n// * ... += 1       // 无论哪种情况，解引用后 +1\n```\n\n> `entry()` + `or_insert()` 是 HashMap 里最常用的模式——代替了\"先 get 判空，再 insert\"的两步操作。\n\n### 2.2 单词频率统计（借用 key）\n\n```rust\nfn word_frequency(text: &str) -> HashMap<&str, i32> {\n    let mut hash = HashMap::new();\n    for word in text.split_whitespace() {\n        *hash.entry(word).or_insert(0) += 1;\n    }\n    hash\n}\n```\n\n> 返回 `HashMap<&str, i32>` 而不是 `HashMap<String, i32>`——key 是从 `text` 借来的切片引用，避免每个单词都分配新 String。\n\n### 2.3 找 value 最大的 key\n\n```rust\nfn max_value_key(map: &HashMap<String, i32>) -> Option<&String> {\n    map.iter()\n       .max_by_key(|&(_, v)| v)  // 按 value 找最大\n       .map(|(k, _)| k)           // 只取 key\n}\n```\n\n| 步骤 | 方法            | 说明                                        |\n| ---- | --------------- | ------------------------------------------- |\n| 1    | `.iter()`       | 迭代 `(&K, &V)` 键值对                      |\n| 2    | `.max_by_key()` | 按指定字段找最大值，返回 `Option<(&K, &V)>` |\n| 3    | `.map()`        | 解构只取 key                                |\n\n---\n\n## 三、HashSet — 交集\n\n### 3.1 初版（双层 for）\n\n```rust\nfn intersection(a: &Vec<i32>, b: &Vec<i32>) -> Vec<i32> {\n    let mut set = HashSet::new();\n    for i in b {\n        for k in a {\n            if i == k { set.insert(*k); }\n        }\n    }\n    set.into_iter().collect()\n}\n```\n\n### 3.2 改进版（集合运算）\n\n```rust\nfn intersection(a: &Vec<i32>, b: &Vec<i32>) -> Vec<i32> {\n    let set_b: HashSet<_> = b.iter().copied().collect();\n    a.iter()\n     .filter(|x| set_b.contains(x))  // 只在 b 中存在的才保留\n     .copied()\n     .collect::<HashSet<_>>()         // 收集到 HashSet 自动去重\n     .into_iter()\n     .collect()\n}\n```\n\n> 改进版 O(n+m) vs 初版 O(n×m)。`HashSet::contains` 是 O(1) 的。\n\n---\n\n## 四、迭代器组合技\n\n### 4.1 filter + map + collect\n\n```rust\nlet nums = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n\nlet result: Vec<i32> = nums\n    .iter()\n    .filter(|&x| x % 2 != 0)  // 取奇数: 1,3,5,7,9\n    .map(|&x| x * 2)           // 翻倍: 2,6,10,14,18\n    .collect();\n```\n\n### 4.2 flatten — 拍平嵌套\n\n```rust\nlet nested = vec![vec![1, 2], vec![3, 4], vec![5, 6]];\nlet flat: Vec<i32> = nested.into_iter().flatten().collect();\n// [1, 2, 3, 4, 5, 6]\n```\n\n> `flatten()` 把 `Vec<Vec<T>>` 变成 `Vec<T>`，等价于 `flat_map(|v| v)`。\n\n### 4.3 sort + dedup — 排序后去重\n\n```rust\nfn sort_and_dedup(v: &mut Vec<i32>) {\n    v.sort();      // 先排序（dedup 只删相邻重复）\n    v.dedup();     // 删除相邻重复\n}\n```\n\n> `dedup()` 只删除**相邻的**重复元素，所以必须先 `sort()` 让相同元素相邻。\n\n### 4.4 partition — 分组\n\n```rust\nfn partition(v: &Vec<i32>) -> (Vec<i32>, Vec<i32>) {\n    v.iter().partition(|&x| x % 2 == 0)  // (偶数, 奇数)\n}\n```\n\n> `partition` 根据谓词把迭代器分成两个 Vec，一次遍历完成。\n\n---\n\n## 五、文件排序 — `sort_lines_in_file`\n\n```rust\nuse std::fs;\n\nfn sort_lines_in_file(path: &str) -> Result<(), Box<dyn std::error::Error>> {\n    let s = fs::read_to_string(path)?;\n    let mut vec: Vec<String> = s.lines().map(String::from).collect();\n\n    vec.sort();\n    let new_content = vec.join(\"\\n\");\n\n    // 保留末尾换行\n    let new_content = if !vec.is_empty() {\n        format!(\"{}\\n\", new_content)\n    } else {\n        new_content\n    };\n    fs::write(path, new_content)?;\n    Ok(())\n}\n```\n\n**关键步骤：**\n\n1. `s.lines()` — 按行分割，返回 `&str` 切片\n2. `.map(String::from)` — 把 `&str` 转成拥有所有权的 `String`（不然 `s` 被 drop 后切片悬垂）\n3. `vec.sort()` — 排序后 `join`\n4. `Box<dyn Error>` — 返回\"任意类型错误\"，比写死具体类型灵活\n\n---\n\n## 六、Todo CLI — 综合小项目\n\n> 用 serde 持久化到 JSON，支持命令行增删查改。\n\n### 6.1 数据结构\n\n```rust\nuse serde::{Serialize, Deserialize};\n\n#[derive(Serialize, Deserialize, Debug, Clone)]\nstruct Task {\n    id: usize,\n    content: String,\n    done: bool,\n}\n\nconst FILE_PATH: &str = \"tasks.json\";\n```\n\n### 6.2 持久化层\n\n```rust\nfn load_tasks() -> Vec<Task> {\n    let content = match fs::read_to_string(FILE_PATH) {\n        Ok(data) => data,\n        Err(_) => return Vec::new(),  // 文件不存在 → 空列表\n    };\n    serde_json::from_str(&content).unwrap_or_default()\n}\n\nfn save_tasks(tasks: &Vec<Task>) {\n    let json = serde_json::to_string_pretty(tasks).expect(\"序列化失败\");\n    fs::write(FILE_PATH, json).expect(\"写入失败\");\n}\n```\n\n> `unwrap_or_default()` 在反序列化失败时返回空 Vec，容错启动。\n\n### 6.3 业务逻辑\n\n```rust\nfn next_id(tasks: &Vec<Task>) -> usize {\n    tasks.iter().map(|t| t.id).max().unwrap_or(0) + 1\n}\n\nfn add_task(tasks: &mut Vec<Task>, content: String) {\n    tasks.push(Task { id: next_id(tasks), content, done: false });\n}\n\nfn list_tasks(tasks: &Vec<Task>) {\n    for task in tasks {\n        let status = if task.done { \"[✓]\" } else { \"[ ]\" };\n        println!(\"{} {} {}\", status, task.id, task.content);\n    }\n}\n\nfn done_task(tasks: &mut Vec<Task>, id: usize) -> Result<(), String> {\n    if let Some(task) = tasks.iter_mut().find(|t| t.id == id) {\n        task.done = true;\n        Ok(())\n    } else {\n        Err(format!(\"任务 {} 不存在\", id))\n    }\n}\n\nfn clear_done(tasks: &mut Vec<Task>) {\n    tasks.retain(|t| !t.done);  // 保留未完成的\n}\n```\n\n### 6.4 命令行解析\n\n```rust\n#[derive(Debug)]\nenum Command {\n    Add(String),\n    List,\n    Done(usize),\n    Clear,\n}\n\nfn parse_args(args: Vec<String>) -> Result<Command, String> {\n    if args.len() < 2 {\n        return Err(\"用法: todo_cli [add <内容> | list | done <id> | clear]\".to_string());\n    }\n    match args[1].as_str() {\n        \"add\"  => Ok(Command::Add(args.get(2).ok_or(\"需要提供任务内容\")?.clone())),\n        \"list\" => Ok(Command::List),\n        \"done\" => {\n            let id = args.get(2).ok_or(\"需要提供任务 ID\")?.parse()\n                         .map_err(|_| \"ID 不是有效整数\")?;\n            Ok(Command::Done(id))\n        }\n        \"clear\" => Ok(Command::Clear),\n        _       => Err(\"未知命令\".to_string()),\n    }\n}\n```\n\n### 6.5 main 组装\n\n```rust\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    let args: Vec<String> = env::args().collect();\n    let command = parse_args(args)?;\n\n    let mut tasks = load_tasks();\n    match command {\n        Command::Add(content) => { add_task(&mut tasks, content); save_tasks(&tasks); }\n        Command::List         => list_tasks(&tasks),\n        Command::Done(id)     => { done_task(&mut tasks, id)?; save_tasks(&tasks); }\n        Command::Clear        => { clear_done(&mut tasks); save_tasks(&tasks); }\n    }\n    Ok(())\n}\n```\n\n**用法：**\n\n```bash\ncargo run add \"买牛奶\"\ncargo run list          # [ ] 1 买牛奶\ncargo run done 1\ncargo run clear\n```\n\n---\n\n## 七、本日小结\n\n| 知识点                         | 说明                                       |\n| ------------------------------ | ------------------------------------------ |\n| 封装 Vec 为自定义类型          | `MyStack` 四方法，`peek` 返回引用的意义    |\n| `HashMap::entry().or_insert()` | 一次操作完成\"查+插\"，返回 `&mut V`         |\n| `&str` 作为 HashMap key        | 从原字符串借用，避免多分配 String          |\n| `max_by_key`                   | 按指定字段找最大，返回 `Option<(&K, &V)>`  |\n| `HashSet` 交集                 | `contains()` O(1)，比双层 for O(n²) 快得多 |\n| `flatten`                      | `Vec<Vec<T>>` → `Vec<T>`                   |\n| `sort` + `dedup`               | 先排序再删相邻重复（顺序不能反）           |\n| `partition`                    | 谓词分组，一次遍历返回两个 Vec             |\n| `Box<dyn Error>`               | 返回\"任意错误类型\"，适合顶层函数           |\n| serde JSON 持久化              | `Serialize` + `Deserialize` derive         |\n| `env::args()`                  | 命令行参数解析                             |\n| CLI 架构                       | parse → load → 业务 → save                 |\n\n**明天：** 继续阶段 3，Maybe 开始阶段 4（泛型/trait/lifetime 综合）。",
            "url": "https://kyriechao.github.io/posts/2026/08/1/12-rust-day-13",
            "title": "Rust 学习计划",
            "summary": "Rust 第13天学习 — 阶段 3：集合、迭代器与 Todo CLI",
            "date_modified": "2026-08-12T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/11-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n学习内容：**递归函数 + 程序构建过程**\n\n#### 递归函数\n\n**两个关键点缺一不可：** 递推关系（大→小） + 结束条件（Base Case）。\n\n底层原理：每次调用在**栈区**分配栈帧（Stack Frame），存放返回地址、形参、局部变量。递���过深 → 栈空间耗尽 → **Stack Overflow**。\n\n| 例子     | 递推关系                      | 复杂度               |\n| -------- | ----------------------------- | -------------------- |\n| 阶乘     | `n! = n × (n-1)!`             | O(n)                 |\n| 斐波那契 | `F(n) = F(n-1) + F(n-2)`      | O(2^n)，大量重复计算 |\n| 汉诺塔   | n-1→辅助，最大→目标，n-1→目标 | O(2^n)               |\n\n> 简单用循环，分治/回溯用递归。不要为了递归而递归。汉诺塔是非递归极其困难、递归极其优雅的典型案例。\n\n#### C 程序构建四工序\n\n从 `.c` 到可执行文件，四道工序：\n\n```\nhello.c  →  ①预处理  →  hello.i  →  ②编译  →  hello.s  →  ③汇编  →  hello.o  →  ④链接  →  hello\n```\n\n| 阶段     | 负责者   | 输入 → 输出        | 做什么                                                       | GCC 命令 |\n| -------- | -------- | ------------------ | ------------------------------------------------------------ | -------- |\n| ① 预处理 | 预处理器 | `.c` → `.i`        | 删注释、处理 `#` 命令（`#include` copy 头文件、`#define` 宏替换、条件编译） | `gcc -E` |\n| ② 编译   | 编译器   | `.i` → `.s`        | C → 汇编代码，语法错误在此报                                 | `gcc -S` |\n| ③ 汇编   | 汇编器   | `.s` → `.o`        | 汇编 → 机器码，生成目标文件                                  | `gcc -c` |\n| ④ 链接   | 链接器   | `.o` + 库 → 可执行 | 合并目标文件，解析符号引用（如 `printf` 地址），分静态链接和动态链接 | `gcc`    |\n\n几个要点：\n\n- **`#include <file>` vs `\"file\"`** — 尖括号搜系统目录，双引号先搜当前目录\n- **头文件保护** — 每个 `.h` 标配 `#ifndef` / `#define` / `#endif`，防止重复包含\n- **头文件放声明性语句** — 函数声明、`extern` 全局变量、`#define` 宏、类型声明\n- **`gcc hello.c -o hello`** — 一步到位，四合一\n\n### Rust\n\n学习内容：[Rust Day12 笔记](11-rust-day-12.md)——**阶段 2：枚举、Result、Option、自定义错误、文件 I/O**\n\n今天在 `demo3.rs` 里写了阶段 2 的练习：\n\n| #    | 练习                           | 涉及点                          |\n| ---- | ------------------------------ | ------------------------------- |\n| 1    | `Shape` 枚举 + `area()`        | `match` + 海伦公式              |\n| 2    | `Operation` 枚举 + `execute()` | `match` + `Result` + 除零检查   |\n| 3    | `parse_int` / `parse_float`    | 字符串解析，`map_err`           |\n| 4    | `calculate(\"a/b\")`             | `?` 链式调用                    |\n| 5    | `find_in_vec`                  | `iter().position()`             |\n| 6    | `double_if_even`               | `Option::filter().map()` 组合   |\n| 7    | `get_or_zero`                  | `unwrap_or()`                   |\n| 8    | `read_file`                    | `match` + `ErrorKind::NotFound` |\n| 9    | `copy_file`                    | `fs::read` + `fs::write`        |\n| 10   | `MyError` 自定义错误           | `impl Display` + `impl Error`   |\n| 11   | `read_config`                  | 文件读取 + io 错误 → 自定义错误 |\n| 12   | `sum_numbers`                  | `for` + `?`，任何一步失败就短路 |\n| 13   | `while let`                    | 遍历 `Vec<Option<i32>>`         |\n\n几个新收获：\n\n- `ErrorKind::NotFound` 的模式匹配写法：`Err(e) if e.kind() == ErrorKind::NotFound`\n- 自定义错误类型需要同时 `impl Display` 和 `impl Error`\n- `Option::filter()` 可以直接筛 `Some` 里的值，不符合变 `None`\n- `while let Some(x) = iter.next()` 处理嵌套 Option 比 `for` + 判空简洁\n\n## 明日计划\n\n1. C 语言：跟课堂节奏，递归章节作业\n2. Rust：阶段 2 收尾，进入阶段 3",
            "url": "https://kyriechao.github.io/posts/2026/08/1/11-diary",
            "title": "2026-08-11 周二 · 递归、构建过程与阶段 2",
            "summary": "C 语言递归函数 + 程序构建四工序（预处理→编译→汇编→链接），Rust 阶段 2 枚举、Result、自定义错误",
            "date_modified": "2026-08-11T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/11-rust-day-12",
            "content_html": "\n# Rust Day12 — 阶段 2：枚举、Result、Option、错误处理\n\n---\n\n## 一、枚举 + `match` — `Shape` 与 `Operation`\n\n### 1.1 Shape 枚举 — 多态计算面积\n\n```rust\nuse std::f64::consts::PI;\n\nenum Shape {\n    Circle(f64),             // 半径\n    Rectangle(f64, f64),     // 宽, 高\n    Triangle(f64, f64, f64), // 三边\n}\n\nimpl Shape {\n    fn area(&self) -> f64 {\n        match &self {\n            Shape::Circle(r) => PI * r * r,\n            Shape::Rectangle(w, h) => w * h,\n            Shape::Triangle(a, b, c) => {\n                let p = (a + b + c) / 2.0;\n                ((p * (p - a) * (p - b) * (p - c)) as f64).sqrt()\n            }\n        }\n    }\n}\n```\n\n> 海伦公式：`S = √(p(p-a)(p-b)(p-c))`，`p` 是半周长。每种 Shape 携带的数据不同，`match` 编译期保证每一种都被处理。\n\n### 1.2 Operation 枚举 — 带错误的 execute\n\n```rust\nenum Operation {\n    Add(f64, f64),\n    Sub(f64, f64),\n    Mul(f64, f64),\n    Div(f64, f64),\n}\n\nimpl Operation {\n    fn execute(&self) -> Result<f64, String> {\n        match &self {\n            Operation::Add(a, b) => Ok(a + b),\n            Operation::Sub(a, b) => Ok(a - b),\n            Operation::Mul(a, b) => Ok(a * b),\n            Operation::Div(a, b) => {\n                if b.eq(&0.0) {\n                    return Err(\"除数不能为零\".to_string());\n                }\n                Ok(a / b)\n            }\n        }\n    }\n}\n```\n\n> 浮点数比较用 `b.eq(&0.0)` 而不是 `b == 0.0`——`f64` 未实现 `Eq`，`==` 会触发 clippy 警告。\n\n---\n\n## 二、`?` 链式调用 — `calculate(\"a/b\")`\n\n### 2.1 底层函数\n\n```rust\nfn parse_float(s: &str) -> Result<f64, String> {\n    s.parse::<f64>().map_err(|_| format!(\"'{}' 不是数字\", s))\n}\n\nfn safe_divide(a: f64, b: f64) -> Result<f64, String> {\n    if b.eq(&0.0) {\n        return Err(\"除数不能为零\".to_string());\n    }\n    Ok(a / b)\n}\n```\n\n### 2.2 组合\n\n```rust\nfn calculate(expr: &str) -> Result<f64, String> {\n    let mut s = expr.trim().split(\"/\");\n    let a = parse_float(s.next().ok_or(\"格式错误：缺少被除数\")?)?;\n    let b = parse_float(s.next().ok_or(\"格式错误：缺少除数\")?)?;\n    safe_divide(a, b)\n}\n```\n\n**双层 `?` 拆解：**\n\n```rust\ns.next().ok_or(\"格式错误\")?      // ?₁：Option → Result，None 时短路\nparse_float(...)?                // ?₂：Result → Result，Err 时短路\n```\n\n> `ok_or` 把 `Option` 转成 `Result`，然后外层 `?` 传播错误；内层 `?` 传播 parse 失败。两次串成一条短路链。\n\n---\n\n## 三、Option 组合子\n\n```rust\nfn find_in_vec(v: &Vec<i32>, target: i32) -> Option<usize> {\n    v.iter().position(|&x| x == target)\n}\n\nfn double_if_even(n: Option<i32>) -> Option<i32> {\n    n.filter(|&x| x % 2 == 0)   // 不是偶数 → None\n     .map(|x| x * 2)             // 是偶数 → 翻倍\n}\n\nfn get_or_zero(n: Option<i32>) -> i32 {\n    n.unwrap_or(0)\n}\n```\n\n| 方法           | 作用                       |\n| -------------- | -------------------------- |\n| `position()`   | 找第一个满足条件的索引     |\n| `filter()`     | `Some` 中值不满足 → `None` |\n| `map()`        | 只碰 `Some` 里的值         |\n| `unwrap_or(d)` | `None` 给默认值，不 panic  |\n\n---\n\n## 四、文件 I/O + 错误处理\n\n### 4.1 match 守卫 `ErrorKind`\n\n```rust\nuse std::fs;\nuse std::io::ErrorKind;\n\nfn read_file(path: &str) {\n    match fs::read_to_string(path) {\n        Ok(con) => println!(\"{con}\"),\n        Err(e) if e.kind() == ErrorKind::NotFound => println!(\"文件不存在: {}\", path),\n        Err(e) => println!(\"读取失败: {}: {}\", path, e),\n    }\n}\n```\n\n> `Err(e) if e.kind() == ...` — match 守卫，只在模式匹配 + 条件都满足时命中。\n\n### 4.2 文件复制\n\n```rust\nfn copy_file(src: &str, dst: &str) -> Result<u64, io::Error> {\n    let content = fs::read(src)?;    // 读\n    let len = content.len() as u64;\n    fs::write(dst, content)?;         // 写\n    Ok(len)\n}\n```\n\n> `fs::read` 和 `fs::write` 适合小文件场景——一次性读入/写出，简单直接。\n\n---\n\n## 五、自定义错误类型\n\n### 5.1 定义 + 实现 trait\n\n```rust\nuse std::error::Error;\nuse std::fmt;\n\n#[derive(Debug)]\nenum MyError {\n    NotFound,\n    InvalidInput(String),\n}\n\nimpl fmt::Display for MyError {\n    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {\n        match self {\n            MyError::NotFound => write!(f, \"文件未找到\"),\n            MyError::InvalidInput(msg) => write!(f, \"无效输入: {}\", msg),\n        }\n    }\n}\n\nimpl Error for MyError {}  // 空体——继承 Debug + Display 即可\n```\n\n> 自定义错误三步：① 定义枚举 ② `impl Display` ③ `impl Error`（通常空体）。`#[derive(Debug)]` 是必须的。\n\n### 5.2 io 错误 → 自定义错误映射\n\n```rust\nfn read_config(path: &str) -> Result<String, MyError> {\n    match fs::read_to_string(path) {\n        Ok(c) => Ok(c),\n        Err(e) if e.kind() == ErrorKind::NotFound => Err(MyError::NotFound),\n        Err(e) => Err(MyError::InvalidInput(e.to_string())),\n    }\n}\n```\n\n> 关键是**错误类型转换**——把标准库的 `io::Error` 映射为自己的 `MyError`，让调用方只关心自己定义的错误类型。\n\n---\n\n## 六、`for` + `?` — 批量操作遇错即停\n\n```rust\nfn sum_numbers(inputs: Vec<&str>) -> Result<i32, String> {\n    let mut sum = 0;\n    for i in inputs {\n        sum += parse_number(i)?;  // 任何一个失败 → 整个函数返回 Err\n    }\n    Ok(sum)\n}\n```\n\n> `?` 不只是用在 `match` 或 `if let` 里——`for` 循环里同样好用。这是 Rust \"遇错即停\"模式的常见写法。\n\n---\n\n## 七、`while let` — 遍历嵌套 Option\n\n```rust\nlet maybe_nums = vec![Some(1), None, Some(3)];\n\nlet mut iter = maybe_nums.into_iter();\nwhile let Some(x) = iter.next() {\n    println!(\"{:?}\", x);  // Some(1), None, Some(3) 逐个打印\n}\n```\n\n> 替代写法：`maybe_nums.into_iter().flatten()` — 自动过滤 `None`，只保留 `Some` 里的值。\n\n---\n\n## 八、本日小结\n\n| 知识点                      | 说明                               |\n| --------------------------- | ---------------------------------- |\n| `enum` + `match`            | 携带不同数据的枚举，编译期穷尽检查 |\n| `impl` 枚举方法             | `impl Shape { fn area(&self) }`    |\n| `?` 双层短路                | `ok_or(...)?` + `parse(...)?`      |\n| `Option::filter().map()`    | 组合子链式处理                     |\n| `unwrap_or()`               | None 给默认值，不 panic            |\n| `Err(e) if e.kind() == ...` | match 守卫                         |\n| 自定义错误                  | `impl Display` + `impl Error`      |\n| io 错误映射                 | 标准库错误 → 自定义错误类型        |\n| `for` + `?`                 | 批量遇错即停                       |\n| `while let`                 | 遍历嵌套 Option                    |\n\n**明天：** 阶段 2 收尾，进入阶段 3。\n\n",
            "url": "https://kyriechao.github.io/posts/2026/08/1/11-rust-day-12",
            "title": "Rust 学习计划",
            "summary": "Rust 第12天学习 — 阶段 2：枚举、Result、Option、自定义错误、文件 I/O",
            "date_modified": "2026-08-11T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/10-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n学习内容：**变量的作用域和生存期**\n\n#### 理论要点\n\n| 变量类型       | 关键字   | 作用域   | 生存期           | 默认初值 |\n| -------------- | -------- | -------- | ---------------- | -------- |\n| 非静态局部变量 | （默认） | 代码块内 | 代码块执行期间   | 随机值   |\n| 静态局部变量   | `static` | 代码块内 | 程序整个执行期间 | 0        |\n| 非静态全局变量 | （默认） | 整个项目 | 程序整个执行期间 | 0        |\n| 静态全局变量   | `static` | 当前文件 | 程序整个执行期间 | 0        |\n\n两个原则：**作用域尽量小、生存期尽量短**。能用局部不用全局，能用非静态不用静态。\n\n多文件编程基础：`.h` 放声明 + `extern` 全局变量，`.c` 放实现，编译时所有 `.c` 一起编译。\n\n#### 今天写的代码\n\n**1. 统计二进制位中 1 的个数（两种实现）：**\n\n```c\n// 方法一：逐位检查\nint counter_binary(char *p, int n) {\n    int count = 0;\n    for (int i = 0; i < n; i++) {\n        unsigned char byte = p[i];\n        for (int k = 7; k >= 0; k--) {\n            if (byte & (1 << k)) count += 1;\n        }\n    }\n    return count;\n}\n\n// 方法二：查表法（每半个字节查一次，O(n) 但常数小）\nint counter_binary2(const unsigned char *p, int n) {\n    int count = 0;\n    const int popcount[16] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};\n    for (int i = 0; i < n; i++) {\n        unsigned char byte = p[i];\n        count += popcount[byte & 0x0F];   // 低 4 位\n        count += popcount[byte >> 4];      // 高 4 位\n    }\n    return count;\n}\n```\n\n> 查表法用空间换时间，一个字节拆成两个 nibble（半字节），每个查一次 16 元素的表。\n\n**2. 冒泡排序、`strcpy`、`strcmp` 手写实现**\n\n**3. `sizeof` 与指针运算：**\n\n```c\nchar str[] = \"memory\";\nchar *p = str + 1;\nprintf(\"%lu\\n\", sizeof(str));  // 7（数组，包含 '\\0'）\nprintf(\"%lu\\n\", sizeof(p));    // 4（指针，32 位平台）\nprintf(\"%lu\\n\", sizeof(a));    // 8（struct：char 1 + 对齐 3 + int 4）\n\nunsigned char *p1 = (unsigned char *)0x80100000;\nprintf(\"%x\\n\", p1 + 5);        // 0x80100005\n```\n\n### Rust\n\n学习内容：[Rust Day11 笔记](10-rust-day-11.md)——**格式化宏 + 阶段 0 热身 + 阶段 1 所有权与借用**\n\n#### 格式化宏\n\nRust 用 `{}` 替代 C 的 `%`，核心区分两个 trait：`Display`（`{}`，面向用户）和 `Debug`（`{:?}`，面向开发者）。完整格式说明符：`{ [参数] : [填充] [对齐] [符号] [#] [0] [宽度] [.精度] [类型] }`。\n\n#### 阶段 0 热身（10 题）✅\n\n温/摄氏度转换、BMI 计算、闰年判断、乘法表、最大公约数/最小公倍数、素数判断、阶乘 & 斐波那契、菱形打印、猜数字——10 道题覆盖基础语法。\n\n#### 阶段 1 所有权与借用 ✅\n\n今天全部走完，逐个概念拆开练习，按理解顺序排列：\n\n| #    | 概念               | 一句话                                       |\n| ---- | ------------------ | -------------------------------------------- |\n| 1    | 所有权转移         | 传参后原变量不能再用                         |\n| 2    | 不可变借用 `&T`    | 借出去还能要回来，可同时多个                 |\n| 3    | 可变借用 `&mut T`  | 同一时刻只能有一个，且不能和 `&T` 共存       |\n| 4    | `&str` 切片        | 比 `&String` 更通用，`String` 和字面量都能传 |\n| 5    | 生命周期 `'a`      | 标注引用有效范围，编译期防止悬垂引用         |\n| 6    | `take_ownership`   | `String` → `Vec<char>`，原值被消费           |\n| 7    | 返回切片引用       | `first_word`：返回 `&str`，不复制数据        |\n| 8    | `swap`             | 通过 `&mut` 交换值，需解引用 `*a`            |\n| 9    | `&[T]` 切片        | Vec 和数组自动转切片，函数参数首选           |\n| 10   | `while` 删除模式   | 删除不加 i，不删才加 i——替代有 bug 的 `for`  |\n| 11   | 迭代器链           | `filter` + `copied` + `collect`，返回新 Vec  |\n| 12   | `Option<(&T, &T)>` | 返回多个引用的首尾元素                       |\n| 13   | 字符串切片         | `find(char::is_whitespace)` 找位置切分       |\n| 14   | 字符串反转         | `chars().rev().collect()`，一行              |\n\n**核心规则：** 一个值一个所有者 → 传参会转移 → 不想转移就用 `&` 借用 → 需要改就用 `&mut` → 返回引用加生命周期 → 视图用切片。\n\n几个印象深的点：\n\n- `for i in 0..v.len()` + `v.remove(i)` 有 bug：`v.len()` 不变但数组变短，索引越界 + 跳元素\n- `while i < v.len()` + 删了不 `i++`：删完后下一个元素自动移到当前位置\n- `&str` 为什么总比 `&String` 好：`&String` 只能传 `String` 的引用，`&str` 可以传 `&String`、字符串字面量、切片——范围大得多\n\n## 思考\n\n今天突然意识到一件事：**我似乎一直在要求自己的思考必须被输出。**\n\n每天写日志，慢慢形成了一种习惯——如果几天没有输出什么思考，就会觉得自己应该产生一点什么，于是强迫自己去思考、去记录。\n\n这有点像薛定谔的猫：在我把思考写下来之前，我似乎无法确认自己今天到底有没有产生值得记录的思考。于是我会主动\"打开盒子\"，强迫它变成一个确定的结果。\n\n但今天特别的地方在于，我第一次比较清楚地**知道自己到底在说什么**。以前可能只是\"感觉不对\"，却很难准确描述。今天我能把它说出来：\n\n**我似乎不是强迫自己思考，而是在强迫思考必须留下可观察、可输出的结果。**\n\n也许思考本身不需要每天都有结果。有时候没有什么值得记录的东西，也可以只是没有什么值得记录的东西。\n\n至少今天，我知道了自己在想什么。\n\n## 明日计划\n\n1. C 语言：跟课堂节奏，继续变量/函数章节\n2. Rust：进入阶段 2（结构体、枚举、错误处理、集合）",
            "url": "https://kyriechao.github.io/posts/2026/08/1/10-diary",
            "title": "2026-08-10 周一 · 阶段 0 & 1 完成",
            "summary": "C 语言变量作用域与生存期，Rust 格式化宏 + 阶段 0 热身 + 阶段 1 所有权与借用全部完成",
            "date_modified": "2026-08-10T13:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/1/10-rust-day-11",
            "content_html": "\n# Rust Day11 — 格式化、热身与所有权\n\n---\n\n## 一、格式化宏\n\nRust 的格式化宏（`println!`、`format!`）在概念上与 C 的 `printf` 类似，但语法更现代，且**在编译时**进行检查。占位符使用花括号 `{}` 而不是 `%`。\n\n完整语法：\n\n```text\n{ [参数] : [填充字符] [对齐方式] [符号] [#] [0] [宽度] [.精度] [类型] }\n```\n\n> `[]` 内的部分都是可选的。\n\n### 1.1 占位符基础\n\n```rust\n// 默认占位符 —— 使用 Display trait\nprintln!(\"{} 天\", 31);  // 31 天\n\n// 位置参数 —— 可重复、可调整顺序\nprintln!(\"{0}, 这是 {1}。{1}, 这是 {0}\", \"爱丽丝\", \"鲍勃\");\n\n// 命名参数\nprintln!(\"{subject} {verb} {object}\",\n    object=\"那只懒狗\",\n    subject=\"那只敏捷的棕色狐狸\",\n    verb=\"跳过\");\n```\n\n### 1.2 格式说明符速查\n\n| 组成部分 | Rust 语法                  | C 类比   |\n| -------- | -------------------------- | -------- |\n| 填充字符 | `{:-<10}`                  | 无       |\n| 对齐方式 | `<` 左 / `>` 右 / `^` 居中 | `-` 标志 |\n| 符号     | `+` 始终显示 / `-` 仅负号  | `+` 标志 |\n| 备用格式 | `#`（如 `{:#x}` → `0xff`） | `#` 标志 |\n| 零填充   | `0`                        | `0` 标志 |\n| 宽度     | 数字或 `{n}$`              | 数字     |\n| 精度     | `.数字`（如 `{:.2}`）      | `.数字`  |\n\n### 1.3 类型（转换字符）\n\n| Rust      | C 类比      | 说明                          |\n| --------- | ----------- | ----------------------------- |\n| （无）    | `%s`        | `Display` trait，用户友好输出 |\n| `?`       | 无          | `Debug` trait，调试输出       |\n| `#?`      | 无          | 美化 Debug 输出               |\n| `b`       | 无          | 二进制                        |\n| `o`       | `%o`        | 八进制                        |\n| `x` / `X` | `%x` / `%X` | 十六进制                      |\n| `p`       | `%p`        | 指针地址                      |\n\n### 1.4 `Display` vs `Debug`\n\n| Trait     | 占位符 | 用途       | 实现方式                       |\n| --------- | ------ | ---------- | ------------------------------ |\n| `Display` | `{}`   | 面向用户   | 需手动实现                     |\n| `Debug`   | `{:?}` | 面向开发者 | 可 `#[derive(Debug)]` 自动派生 |\n\n```rust\n#[derive(Debug)]\nstruct Point { x: i32, y: i32 }\n\nlet p = Point { x: 10, y: 20 };\nprintln!(\"{:?}\", p);   // Point { x: 10, y: 20 }\n// println!(\"{}\", p);  // 错误！没有实现 Display\n```\n\n> Rust **没有** C 的长度修饰符（`%ld`、`%zu`），类型信息在编译时已知，无需手动指定。\n\n---\n\n## 二、阶段 0：热身（10 题）\n\n> 熟悉基础语法，每道题都是独立的 `main()` 函数。\n\n### 2.1 题目清单\n\n| #    | 练习                    | 涉及知识点                             |\n| ---- | ----------------------- | -------------------------------------- |\n| 1    | 摄氏度 → 华氏度         | `read_line` + `parse` + `flush`        |\n| 2    | BMI 计算                | `split_whitespace` 解析多个输入        |\n| 3    | 闰年判断                | `if` + 逻辑运算符                      |\n| 4    | 乘法表                  | 嵌套 `for` + `{:02}` 宽度格式化        |\n| 5    | 最大公约数 & 最小公倍数 | 辗转相除 `gcd2` + 穷举 `gcd`           |\n| 6    | 素数判断                | `sqrt` 优化 + `step_by(2)` 跳过偶数    |\n| 7    | 阶乘 & 斐波那契         | 递归版 vs 迭代版 `fibonacci2`          |\n| 8    | 菱形打印                | `abs(i - n/2)` 算空格数                |\n| 9    | 猜数字                  | `rand::rng()` + `match` + `Ordering`   |\n| 10   | 输入输出综合            | `io::stdin()` + `io::stdout().flush()` |\n\n### 2.2 核心代码片段\n\n```rust\n// 辗转相除法\nfn gcd2(n: i32, m: i32) -> i32 {\n    let mut m = m.abs();\n    let mut n = n.abs();\n    while m != 0 {\n        let temp = m;\n        m = n % m;\n        n = temp;\n    }\n    n\n}\n\n// 素数判断（sqrt 优化 + 跳偶数）\nfn prime(n: i32) -> bool {\n    if n == 2 { return true; }\n    if n < 2 || n % 2 == 0 { return false; }\n    let limit = (n as f64).sqrt() as i32 + 1;\n    for i in (3..=limit).step_by(2) {\n        if n % i == 0 { return false; }\n    }\n    true\n}\n\n// 菱形打印\nfor i in 0..n {\n    let offset = (i - n / 2).abs();  // 距中心行距离\n    for _ in 0..offset { print!(\" \"); }\n    for _ in 0..(n - 2 * offset) { print!(\"*\"); }\n    println!();\n}\n\n// 猜数字\nlet number = rand::rng().random_range(1..=100);\nloop {\n    let guess: i32 = match num.trim().parse() {\n        Ok(num) => num,\n        Err(_) => continue,  // 非数字 → 重来\n    };\n    match guess.cmp(&number) {\n        Ordering::Less => println!(\"太小了!\"),\n        Ordering::Greater => println!(\"太大了!\"),\n        Ordering::Equal => { println!(\"你赢了!\"); break; }\n    }\n}\n```\n\n---\n\n## 三、阶段 1：所有权与借用（完成）\n\n> 逐个概念拆开练习，从所有权转移到切片，全部跑通。\n\n### 3.1 所有权转移\n\n```rust\nfn print_length(s: String) {\n    println!(\"长度: {}\", s.len());\n}  // s 在此被 drop\n\nfn main() {\n    let s = String::from(\"hello\");\n    print_length(s);\n    // println!(\"{s}\");  // ❌ s 的所有权已经被移走\n}\n```\n\n### 3.2 不可变借用 `&T`\n\n```rust\nfn print_length(s: &String) {\n    println!(\"长度: {}\", s.len());\n}\n\nfn main() {\n    let s = String::from(\"hello\");\n    print_length(&s);       // 借出去\n    println!(\"{}\", s);      // ✓ 还能用，只是借了一下\n}\n```\n\n### 3.3 可变借用 `&mut T`\n\n```rust\nfn append_exclamation(s: &mut String) {\n    s.push_str(\"!\");\n}\n\nfn main() {\n    let mut s = String::from(\"hello\");\n    append_exclamation(&mut s);\n    println!(\"{}\", s);  // \"hello!\"\n}\n```\n\n### 3.4 `&str` — 字符串切片\n\n```rust\nfn get_length(s: &str) -> usize {\n    s.len()\n}\n\nfn main() {\n    let s = String::from(\"hello\");\n    let len1 = get_length(&s);       // &String 自动转 &str（Deref 强制转换）\n    let len2 = get_length(\"world\");  // 字面量本身就是 &str\n    println!(\"{} {}\", len1, len2);\n}\n```\n\n> `&String` 可以自动转为 `&str`，因此函数参数写 `&str` 比 `&String` 更通用。\n\n### 3.5 生命周期标注\n\n```rust\nfn longer<'a>(a: &'a str, b: &'a str) -> &'a str {\n    if a.len() > b.len() { a } else { b }\n}\n\nfn main() {\n    let s1 = String::from(\"short\");\n    let s2 = String::from(\"loooooong\");\n    let result = longer(&s1, &s2);\n    println!(\"{}\", result);  // \"loooooong\"\n}\n```\n\n> 当返回值是引用时，编译器需要知道它来自哪个参数——`'a` 告诉编译器：返回值与两个参数活得一样久。\n\n### 3.6 所有权转换：`String` → `Vec<char>`\n\n```rust\nfn take_ownership(s: String) -> Vec<char> {\n    s.chars().collect()\n}\n\nfn main() {\n    let s = String::from(\"hello\");\n    let chars = take_ownership(s);\n    // println!(\"{s}\");  // ❌ s 已死\n    println!(\"{:?}\", chars);  // ['h', 'e', 'l', 'l', 'o']\n    // chars 拥有的是从 s 中拆出来的字符，s 本身已被消费\n}\n```\n\n### 3.7 返回切片的引用 — `first_word`\n\n```rust\nfn first_word(s: &str) -> &str {\n    match s.find(char::is_whitespace) {\n        Some(index) => &s[..index],\n        None => s,\n    }\n}\n\nfn main() {\n    let s = String::from(\"hello world\");\n    let word = first_word(&s);\n    println!(\"{}\", word);       // \"hello\"\n\n    let s2 = \"no_space\";\n    println!(\"{}\", first_word(s2));  // \"no_space\"\n}\n```\n\n> 返回的 `&str` 是从原字符串\"借\"出来的一个视图，不复制数据。\n\n### 3.8 `swap` — 通过可变引用交换值\n\n```rust\nfn swap(a: &mut i32, b: &mut i32) {\n    let temp = *a;   // 解引用取值\n    *a = *b;         // 解引用赋值\n    *b = temp;\n}\n\nfn main() {\n    let mut x = 5;\n    let mut y = 10;\n    swap(&mut x, &mut y);\n    println!(\"x = {}, y = {}\", x, y);  // x = 10, y = 5\n}\n```\n\n### 3.9 切片 `&[T]` — 数组和 Vec 通用\n\n```rust\nfn sum_slice(nums: &[i32]) -> i32 {\n    let mut sum = 0;\n    for i in nums { sum += i; }\n    sum\n}\n\nfn main() {\n    let v = vec![1, 2, 3, 4, 5];\n    println!(\"{}\", sum_slice(&v));       // 15（Vec 自动转切片）\n\n    let arr = [10, 20, 30];\n    println!(\"{}\", sum_slice(&arr));     // 60（数组也能当切片传）\n}\n```\n\n> `&[T]` 是 Rust 里最通用的\"连续数据视图\"——Vec 和数组都能自动转为它。\n\n### 3.10 `while i < v.len()` 删除模式\n\n**为什么 `for i in 0..v.len()` + `v.remove(i)` 有 bug？**\n\n因为 `v.len()` 在循环开始时就被固定了，`remove` 之后数组长度变了但 `v.len()` 不变，导致：\n\n1. 索引越界\n2. 删除后后续元素前移，下一次 `i++` 会跳过一个元素\n\n**正确做法：**\n\n```rust\nfn clear_short(v: &mut Vec<String>) {\n    // 原地删除长度 < 3 的字符串\n    let mut i: usize = 0;\n    while i < v.len() {\n        if v[i].len() < 3 {\n            v.remove(i);  // 删除后不 i++，因为下一个元素移到当前位置了\n        } else {\n            i += 1;\n        }\n    }\n}\n\nfn remove_even(v: &mut Vec<i32>) {\n    let mut i = 0;\n    while i < v.len() {\n        if v[i] % 2 == 0 {\n            v.remove(i);\n        } else {\n            i += 1;\n        }\n    }\n}\n```\n\n> 口诀：**删了就不加，不删才加一。**\n\n### 3.11 迭代器链 — filter + copied + collect\n\n```rust\nfn get_even(nums: &Vec<i32>) -> Vec<i32> {\n    nums.iter()\n        .filter(|&&x| x % 2 == 0)\n        .copied()\n        .collect()\n}\n```\n\n### 3.12 `Option<(&T, &T)>` — 返回多个引用\n\n```rust\nfn get_first_and_last(v: &Vec<i32>) -> Option<(&i32, &i32)> {\n    if v.is_empty() {\n        return None;\n    }\n    Some((&v[0], &v[v.len() - 1]))\n}\n```\n\n### 3.13 字符串切片 — `split_at_space`\n\n```rust\nfn split_at_space(s: &str) -> (&str, &str) {\n    match s.find(char::is_whitespace) {\n        Some(index) => (&s[..index], &s[index + 1..]),\n        None => (s, \"\"),\n    }\n}\n```\n\n### 3.14 字符串反转 — 一行\n\n```rust\nfn reverse_string(s: &str) -> String {\n    s.chars().rev().collect()\n}\n```\n\n---\n\n## 四、阶段 1 核心规则总结\n\n| 规则                 | 说明                                                 |\n| -------------------- | ---------------------------------------------------- |\n| 一个值只有一个所有者 | `let s = ...` 之后，`s` 拥有这个值                   |\n| 所有权转移（move）   | 赋值、传参、返回会转移所有权                         |\n| 借用 `&T`            | 不可变借用，可同时存在多个                           |\n| 可变借用 `&mut T`    | 可变借用，同一时刻只能有一个，且不能和不可变借用共存 |\n| 切片 `&[T]` / `&str` | 不拥有数据，只是\"借用\"一段连续内存的视图             |\n| 生命周期 `'a`        | 标注引用的有效范围，编译器用来自动检查悬垂引用       |\n\n### `String` vs `&str` vs `&String` 速查\n\n| 类型      | 拥有数据？ | 分配在堆？ | 何时用                         |\n| --------- | ---------- | ---------- | ------------------------------ |\n| `String`  | 是         | 是         | 需要修改、拥有数据             |\n| `&str`    | 否（借用） | 否         | 只读访问，函数参数首选         |\n| `&String` | 否（借用） | 否         | 几乎不用，直接用 `&str` 更通用 |\n\n---\n\n## 五、明天计划\n\n进入阶段 2：结构体、枚举、错误处理、集合。",
            "url": "https://kyriechao.github.io/posts/2026/08/1/10-rust-day-11",
            "title": "Rust 学习计划",
            "summary": "Rust 第11天学习 — 格式化宏 + 阶段 0 热身 + 阶段 1 所有权与借用",
            "date_modified": "2026-08-10T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/0/08-diary",
            "content_html": "\n## 技术\n\n### 嵌入式 — LED 控制练习（STC8G）\n\n今天在 Keil 里写了 5 个 C 程序，全部基于 STC8G 单片机，通过 P3 口控制 LED：\n\n| 程序                    | 功能                           | 关键技术                           |\n| ----------------------- | ------------------------------ | ---------------------------------- |\n| **Running_LED**         | 3 个 LED 流水灯（正序 + 逆序） | `sbit` 位定义，软件延时            |\n| **Strobe_LED**          | 单 LED 频闪（10ms 间隔）       | `P3 \\|= (1<<7)` 直接操作端口寄存器 |\n| **Strobe_LED + Buzzer** | 频闪 + 无源蜂鸣器同时工作      | 两个 `sbit` 分别控制不同引脚       |\n| **Breathing_LED**       | 呼吸灯 — 定时器中断产生 PWM    | Timer0 中断，`cnt < pwm` 比较输出  |\n| **Breathing_LED2**      | 呼吸灯 — 纯软件延时版          | 双层 for 循环，渐亮 + 渐暗         |\n\n#### 核心代码片段\n\n**流水灯（正序 + 逆序来回）：**\n\n```c\nwhile (1) {\n    // 正序：LED1→LED2→LED3\n    LED1 = 1; LED2 = 0; LED3 = 0; delay_ms(200);\n    LED1 = 0; LED2 = 1; LED3 = 0; delay_ms(200);\n    LED1 = 0; LED2 = 0; LED3 = 1; delay_ms(200);\n    // 逆序：LED3→LED2→LED1\n    LED1 = 0; LED2 = 0; LED3 = 1; delay_ms(200);\n    LED1 = 0; LED2 = 1; LED3 = 0; delay_ms(200);\n    LED1 = 1; LED2 = 0; LED3 = 0; delay_ms(200);\n}\n```\n\n**呼吸灯 — 软件 PWM 原理：**\n\n```c\n// 定时器每 0.1ms 中断一次\nLED = (cnt < pwm) ? 1 : 0;   // cnt < pwm 时亮，否则灭\nif (++cnt >= 100) {           // 100 级为一个完整周期\n    cnt = 0;\n    if (++cycle >= 5) {       // 每 5 个周期改一次占空比\n        cycle = 0;\n        if (dir) { if (++pwm >= 100) dir = 0; }  // 渐亮\n        else     { if (--pwm == 0) dir = 1; }    // 渐暗\n    }\n}\n```\n\n> PWM 原理：通过调节亮/灭的时间比例（占空比）实现 LED 亮度的平滑变化。`pwm` 值从 0 到 100 再到 0，对应 LED 从灭到最亮再到灭。\n\n#### 寄存器配置笔记\n\n```c\n// P3 口推挽输出\nP3M0 |= 0x80;    // P37 推挽输出（0x80 = 1000 0000）\nP3M1 &= ~0x80;\n\n// 定时器 0 配置\nAUXR |= 0x80;    // 12T 模式\nTMOD = 0x01;     // 定时器 0，16 位模式\nTH0 = 0xFB; TL0 = 0xB4;  // 0.1ms 定时初值\nET0 = 1; EA = 1; TR0 = 1; // 开中断，启动定时器\n```\n\n### Rust\n\n今天没写 Rust。\n\n## 明日计划\n\n周末（周六、周日）休息，不安排学习任务。\n\n## 思考\n\n周五到周日休息，无深入思考。这三天给自己留白——之前聊到的\"持续运行不等于持续成长\"，休息本身就是成长的一部分。",
            "url": "https://kyriechao.github.io/posts/2026/08/0/08-diary",
            "title": "2026-08-08 周五 · LED 驱动练习",
            "summary": "用 STC8G 写了 5 个 LED 控制程序，呼吸灯、流水灯、频闪灯",
            "date_modified": "2026-08-08T13:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/0/05-diary",
            "content_html": "\n## 技术\n\n### C 语言 — 函数\n\n学习内容：**函数（Function）**\n\n#### 核心概念\n\n函数是 C 语言程序的基本构成单位，一个函数就是一个功能模块。两种编程思想：\n\n| 思想         | 核心思路                                                |\n| ------------ | ------------------------------------------------------- |\n| 模块化编程   | 项目 → 拆成若干功能模块 → 每个模块用函数实现 → 组合调用 |\n| 面向过程编程 | 问题 → 拆成若干步骤 → 每个步骤用函数实现 → 按顺序调用   |\n\n函数分为两种：\n\n- **自定义函数** — 自己定义自己用\n- **库函数** — 别人已实现，直接调用（标准 C 库约 300 个函数，如 `printf`、`scanf`）\n\n#### 函数定义语法\n\n```c\n返回值类型 函数名(形参列表)\n{\n    // 函数体\n    // return 表达式;\n}\n```\n\n- 返回值类型可以是任意类型，不需要返回值写 `void`\n- 形参（形式参数）就是变量，多个用逗号隔开；也可以没有（留空或写 `void`）\n- 函数必须先定义或声明，然后才能调用\n\n#### 调用流程\n\n```\n实参初始化形参 → 进入函数体执行 → 遇到 return 或执行完毕 → 返回调用处\n                                                              ↓\n                                          形参和函数内局部变量自动释放\n```\n\n- 实参必须和形参一一对应（个数、类型）\n- `main` 函数由系统自动调用，`main` 返回 → 程序结束\n- `main` 返回值：`0` = 正常结束，非零 = 异常结束\n\n### Rust — 文件 I/O 练习\n\n今天写了三个文件操作函数：\n\n**1. 统计文件行数** — `File::open` + `BufReader::lines()` 逐行迭代\n\n**2. 文件内容替换** — `read_to_string` → `replace` → 先写 `.tmp` 临时文件 → `rename` 原子替换原文件\n\n> 为什么要临时文件 + rename？直接覆盖原文件时如果中途崩溃，原文件就损坏了。\n\n**3. 递归计算目录大小** — `symlink_metadata`（不跟随符号链接，避免死循环），遇无权限文件打警告跳过而不是直接崩溃\n\n## 思考\n\n### 一个人应该依据什么评价自己？\n\n以前人们习惯用外部标准评价自己：成功或失败、他人的认可或否定、财富、地位、结果。但这些评价本质上都是人类赋予的意义，并不是事物天然拥有的属性。\"成功\"和\"失败\"本身也是人的价值体系产生的概念。如果没有人类，没有评价主体，一幅画、一项技术、一次胜利或者失败，都只是一种存在状态。因此，**人的价值不应该完全依赖外界评价。**\n\n### 关于王阳明\"知行合一\"的理解\n\n它不是简单地把大道理全部记住然后强迫自己执行。真正的知行合一，是**内心真正认可某件事情，并且行动自然符合这种认识。** 这里的\"心\"不是普通的情绪和欲望，而是经过良知判断后的内心。如果只是因为舒服所以去做某件事情，并不代表知行合一。例如作弊——可能带来短暂利益和满足，但它违背了自己的原则，因此不是良知上的选择。真正的知行合一，是知道什么是正确的，并且愿意按照这个判断行动，即使没有外部监督。\n\n### 如何认可自己，建立内核\n\n一个人的自信不应该建立在\"我一直成功，所以我相信自己\"——这种自信很脆弱。更稳定的自信来源于：**无论面对成功还是失败，我都有能力面对、调整和继续前进。** 真正认可自己，不是认可每一次结果，而是认可自己的选择过程：是否违背自己的原则、是否持续成长、是否朝自己认可的方向前进。\n\n### 关于坚持初心\n\n\"不忘初心\"并不是永远坚持最初的想法，因为最初的想法可能包含幻想。真正应该坚持的是更深层的东西：表层目标可能改变（想成为某种职业，后来发现路径不适合，可以调整），但深层价值可以保持（想创造东西、想理解世界、想提升自己的能力）。\n\n所以：**改变方法，不一定是背叛初心。** 真正需要判断的是：我是因为认识更深所以改变，还是因为遇到困难所以逃避。\n\n### 对自己的观察\n\n我发现自己已经形成了一些长期坚持：考试一直坚持不作弊，即使没有人监督；持续学习编程；持续写项目；持续思考技术、人生和价值问题；即使没有人关注，也愿意继续做自己认为有意义的事情。这些事情说明：我的行动已经不完全依赖外界评价，而是在建立自己的价值体系。\n\n### 需要改进的地方\n\n但是我也发现：自己还不能完全放松。如果一天完全不学习、不思考、不创造，就会感觉这一天被浪费。我的大脑似乎一直有一个后台程序运行——无论走路、学习甚至睡觉，都在思考问题。这既是一种优势，也可能成为压力。因为**持续运行不等于持续成长。** 人的成长不仅来自行动，也来自休息、沉淀和整合。休息不是浪费时间，而是成长过程的一部分。\n\n真正强大的状态不是\"我永远不能停止\"，而是**\"我可以停下来，因为我知道自己还会继续前进。\"**\n\n### 最后的理解\n\n人的内核不是来自\"我永远正确\"，而来自**\"即使发现自己错误，我也有能力面对并修正。\"** 真正的坚定不是拒绝改变，而是在不断变化中，保持自己认可的方向。评价自己的标准应该逐渐从\"别人是否认可我\"转变为**\"我是否忠于自己的原则，并且持续成为自己认可的人。\"**\n\n## 明日计划\n\n1. C 语言：跟课堂节奏，完成函数章节作业\n2. Rust：打算做个小项目\n\n",
            "url": "https://kyriechao.github.io/posts/2026/08/0/05-diary",
            "title": "2026-08-05 周三 · C 语言函数",
            "summary": "C 语言进入函数章节，Rust 练了文件 I/O",
            "date_modified": "2026-08-05T13:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/0/05-rust-day-10",
            "content_html": "\n# Rust Day10 — 文件 I/O 练习\n\n> 今天没有跟 Rustlings，自己写了三个文件操作函数练手。\n\n---\n\n## 一、统计文件行数 — `BufReader::lines()`\n\n```rust\nuse std::fs::File;\nuse std::io::{BufRead, BufReader};\n\nfn count_lines(path: &str) -> Result<usize, std::io::Error> {\n    let file = File::open(path)?;\n    let reader = BufReader::new(file);\n    let mut count = 0;\n    for _ in reader.lines() {\n        count += 1;\n    }\n    Ok(count)\n}\n```\n\n**逐行拆解：**\n\n| 步骤 | 代码                   | 说明                                   |\n| ---- | ---------------------- | -------------------------------------- |\n| 1    | `File::open(path)?`    | 打开文件，失败时 `?` 直接返回 `Err`    |\n| 2    | `BufReader::new(file)` | 包装成带缓冲的读取器，减少系统调用次数 |\n| 3    | `reader.lines()`       | 返回一个迭代器，每次 `next()` 读一行   |\n| 4    | `for _ in ...`         | 逐行迭代，不关心内容，只计数           |\n\n**`lines()` 的细节：**\n\n- 返回 `io::Result<String>`，每次读取自动处理 `\\n`、`\\r\\n` 跨平台换行\n- 每行末尾的换行符会被自动去掉\n- 遇到读取错误时，下一次迭代返回 `Err`\n\n---\n\n## 二、文件内容替换 — 临时文件 + 原子重命名\n\n```rust\nuse std::fs;\n\nfn replace_in_file(path: &str, from: &str, to: &str) -> Result<(), std::io::Error> {\n    let content = fs::read_to_string(path)?;    // 1. 一次性读入内存\n    let new_content = content.replace(from, to); // 2. 字符串替换\n    let temp_path = format!(\"{}.tmp\", path);\n\n    fs::write(&temp_path, new_content)?;         // 3. 写到临时文件\n    fs::rename(&temp_path, path)?;               // 4. 原子重命名\n    Ok(())\n}\n```\n\n**为什么用临时文件而不是直接覆盖？**\n\n```\n❌ 直接写原文件：\n   打开原文件 → 写入 → 中途崩溃 → 原文件损坏（一半旧一半新）\n\n✅ 临时文件 + rename：\n   写 .tmp → 成功 → rename(原文件) → 失败也不影响原文件\n           → 崩溃 → .tmp 损坏，原文件完好无损\n```\n\n> `rename` 在同一个文件系统上是原子操作——要么成功替换，要么什么都没发生。这是保证数据安全的经典模式。\n\n**`fs::read_to_string` vs `BufReader`：**\n\n| 方法                 | 适用场景                                   |\n| -------------------- | ------------------------------------------ |\n| `read_to_string`     | 文件不大，需要一次性处理全部内容（如替换） |\n| `BufReader::lines()` | 文件很大，逐行处理省内存（如统计行数）     |\n\n---\n\n## 三、递归计算目录大小\n\n```rust\nuse std::fs::{self, read_dir};\nuse std::path::Path;\n\nfn dir_size(path: impl AsRef<Path>) -> Result<u64, std::io::Error> {\n    let path = path.as_ref();\n    let meta = fs::symlink_metadata(path)?;\n\n    // 文件或符号链接 → 直接返回大小\n    if meta.is_symlink() || meta.is_file() {\n        return Ok(meta.len());\n    }\n\n    // 目录 → 递归累加\n    if meta.is_dir() {\n        let mut total = 0;\n        for entry in read_dir(path)? {\n            let entry = entry?;\n            match dir_size(&entry.path()) {\n                Ok(size) => total += size,\n                Err(e) => eprintln!(\"Warning: {}: {}\", entry.path().display(), e),\n            }\n        }\n        Ok(total)\n    } else {\n        Ok(0)\n    }\n}\n```\n\n**三个设计决策：**\n\n### 3.1 `symlink_metadata` 而不是 `metadata`\n\n| 方法               | 跟随符号链接？ | 风险                                |\n| ------------------ | -------------- | ----------------------------------- |\n| `metadata`         | 是             | 符号链接指向自身 → 死循环           |\n| `symlink_metadata` | 否             | 安全，但需要手动判断 `is_symlink()` |\n\n> `symlink_metadata` 获取文件自身的信息，不会跟随符号链接。遇到符号链接直接返回它的大小（通常是路径字符串的字节数），不往下追。\n\n### 3.2 `impl AsRef<Path>` 而不是 `&str`\n\n```rust\n// ✓ 兼容多种传参方式\ndir_size(\"/tmp\");           // &str\ndir_size(String::from(\".\")); // String\ndir_size(Path::new(\".\"));    // &Path\ndir_size(&path_buf);         // &PathBuf\n```\n\n> `AsRef<Path>` 让函数对调用者更友好，标准库里大量使用这种写法。\n\n### 3.3 遇错打印警告而不是直接崩溃\n\n```rust\nmatch dir_size(&entry.path()) {\n    Ok(size) => total += size,\n    Err(e) => eprintln!(\"Warning: {}: {}\", entry.path().display(), e),\n    // 不 return Err —— 跳过一个文件无伤大雅\n}\n```\n\n如果是没有权限访问的子目录（如 Windows 的 `System Volume Information`），跳过它继续统计其余部分，比直接报错退出更实用。\n\n---\n\n## 四、本日小结\n\n| 知识点               | 说明                                      |\n| -------------------- | ----------------------------------------- |\n| `BufReader::lines()` | 逐行迭代，自动处理跨平台换行              |\n| `read_to_string`     | 小文件一次性读入内存                      |\n| `rename` 原子性      | 临时文件 + rename = 安全的文件替换        |\n| `symlink_metadata`   | 不跟随符号链接，防死循环                  |\n| `impl AsRef<Path>`   | 让函数接受多种路径类型                    |\n| 容错处理             | 非致命错误打 warning 跳过，不中断整体流程 |\n\n**明天继续文件 I/O 还是回 Rustlings？** 还剩 smart_pointers、threads、macros、clippy、conversions 五个模块。",
            "url": "https://kyriechao.github.io/posts/2026/08/0/05-rust-day-10",
            "title": "Rust 学习计划",
            "summary": "Rust 第10天学习 — 文件 I/O 练习",
            "date_modified": "2026-08-05T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/0/04-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n学习内容：[C 语言 Day8 笔记](04-rust-day-9.md)——**流程控制结构（下）**\n\n#### break 与 continue\n\n| 语句       | 作用                                 |\n| ---------- | ------------------------------------ |\n| `break`    | 结束当前整个循环                     |\n| `continue` | 跳过本次循环剩余部分，进入下一次迭代 |\n\n#### error vs warning\n\n| 类型    | 严重程度     | 编译结果                   |\n| ------- | ------------ | -------------------------- |\n| error   | 致命语法错误 | 编译失败，不生成可执行文件 |\n| warning | 轻微问题     | 编译成功，但不要无视       |\n\n> 真正的噩梦是**运行时 Bug**——编译通过，运行结果却不对。编译建议：`gcc -Wall` 严格检查。\n\n#### goto 语句\n\n- 无条件跳转到标签（Label）处执行\n- 也能实现循环，但**不要滥用**——会严重降低代码可读性\n- **不能跨函数跳转**\n\n### Rust\n\n学习内容：[Rust Day9 笔记](../Rust/day9)——**并发深入练习**\n\n今天把线程相关的每个概念拆开单独练了一遍，**感觉有些理解了**。之前 Rustlings threads 是照着注释填空，知其然不知其所以然，今天分 7 步逐个跑通：\n\n1. **`spawn` + `join`** — 最基本的线程启停，join 阻塞等待子线程结束\n2. **`move`** — 所有权移入线程，理解为什么必须这样\n3. **`Arc`** — 多线程共享只读数据，`Arc::clone` 只加引用计数\n4. **`Arc<Mutex<T>>`** — 多线程读写同一份数据（10 线程各 +1）\n5. **`mpsc::channel`** — 线程间发消息，关键细节：必须 `drop(tx)` 否则 rx 永远等不到结束\n6. **死锁** — 刻意写了一个死锁：两个线程按相反顺序拿两把锁，互相等待\n7. **`AtomicUsize`** — 简单计数不用锁，硬件原子指令\n\n#### 并发选型速查\n\n| 需求         | 方案                     |\n| ------------ | ------------------------ |\n| 只读共享     | `Arc<T>`                 |\n| 读写共享     | `Arc<Mutex<T>>`          |\n| 线程间传数据 | `mpsc::channel`          |\n| 简单计数     | `AtomicUsize`            |\n| 启动后台任务 | `thread::spawn` + `join` |\n\n核心认知：`Arc` 解决共享、`Mutex` 解决互斥、`channel` 解决通信——三件事分清楚之后组合起来就不神秘了。\n\n## 明日计划\n\n1. C 语言：循环结构作业 + 继续跟课堂\n2. Rust：并发练习加入错误处理（`lock()` 返回的 `Result` 不只用 `unwrap`），有时间看看 `RwLock`",
            "url": "https://kyriechao.github.io/posts/2026/08/0/04-diary",
            "title": "2026-08-04 周一 · 并发初通",
            "summary": "C 语言循环结构与 goto，Rust 并发拆开练了一遍，感觉有些理解了",
            "date_modified": "2026-08-04T13:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/0/04-rust-day-9",
            "content_html": "\n# Rust Day9 — 并发深入练习\n\n今天把线程相关的每个概念拆开单独练了一遍，**感觉有些理解了。** 之前看文档觉得 `Arc<Mutex<T>>` 这套绕，一个个写过去之后发现其实就是三层组合。\n\n---\n\n## 1. 线程基础 — spawn 与 join\n\n### 1.1 最简单的多线程\n\n```rust\nuse std::{thread, time::Duration};\n\nfn download() {\n    println!(\"开始下载...\");\n    thread::sleep(Duration::from_secs(3));\n    println!(\"下载完成!\");\n}\n\nfn main() {\n    // 直接调用：主线程卡 3 秒，期间什么都干不了\n    // download();\n\n    // spawn：子线程后台跑，主线程继续往下\n    thread::spawn(|| {\n        download();\n    });\n    println!(\"界面继续运行\");  // 这行先打印，不等 download\n\n    thread::spawn(|| {\n        println!(\"子线程：开始下载...\");\n        thread::sleep(Duration::from_secs(3));\n        println!(\"子线程：下载完成!\");\n    });\n    println!(\"界面继续运行\");\n    thread::sleep(Duration::from_secs(5));  // 给子线程跑完的时间\n}\n```\n\n**关键认知：**\n\n| 行为                   | 说明                                         |\n| ---------------------- | -------------------------------------------- |\n| `spawn` 立即返回       | 主线程不等子线程，各跑各的                   |\n| main 结束 → 程序退出   | 所有子线程被强制终止                         |\n| `thread::sleep` 等线程 | 简单粗暴但不靠谱（万一子线程需要更长时间？） |\n\n### 1.2 join — 正确等线程的方式\n\n```rust\nlet handle = thread::spawn(|| {\n    thread::sleep(Duration::from_secs(3));\n    println!(\"子线程：下载完成!\");\n});\n\nprintln!(\"主线程：做别的事\");    // 先打印\nhandle.join().unwrap();          // 阻塞，直到子线程跑完\nprintln!(\"主线程：子线程结束了，我也结束了\");\n```\n\n**执行顺序一目了然：**\n\n```\n主线程：做别的事          ← 立即\n子线程：下载完成!         ← 3 秒后\n主线程：子线程结束了...   ← join 结束后\n```\n\n| API        | 作用                                                   |\n| ---------- | ------------------------------------------------------ |\n| `join()`   | 阻塞当前线程，等待 `JoinHandle` 对应的线程结束         |\n| `unwrap()` | 子线程 panic 时 `join` 返回 `Err`，这里直接 panic 传播 |\n\n---\n\n## 2. move — 所有权移入线程\n\n### 2.1 为什么必须 move\n\n```rust\nlet s = String::from(\"hello\");\n\n// ❌ 这样写编译不过：\n// thread::spawn(|| {\n//     println!(\"{}\", s);  // s 是局部变量，可能在线程跑完前就被释放了\n// });\n\n// ✓ move：所有权给闭包，编译器不再担心生命周期\nlet val = s.clone();\nlet handle = thread::spawn(move || {\n    println!(\"{}\", val);\n});\n\nprintln!(\"{}\", s);  // s 还在，因为只移走了 clone 出来的 val\nhandle.join().unwrap();\n```\n\n**move 的决策表：**\n\n| 主线程之后还要用 | 做法                              |\n| ---------------- | --------------------------------- |\n| 还要用           | `clone()` 一份，`move` 移走克隆品 |\n| 不用了           | 直接 `move` 移走原值              |\n| 多线程共享只读   | 用 `Arc` 替代 `move`              |\n\n---\n\n## 3. Arc — 多线程共享一份数据\n\n### 3.1 基础用法\n\n```rust\nuse std::sync::Arc;\n\nlet data = Arc::new(vec![1, 2, 3]);\n\nlet handle = thread::spawn(move || {\n    println!(\"{:?}\", data);\n});\nhandle.join().unwrap();\n```\n\n### 3.2 strong_count — 看引用计数变化\n\n```rust\nlet data = Arc::new(String::from(\"Hello!\"));\nlet data2 = Arc::clone(&data);\nlet data3 = Arc::clone(&data);\n\nlet count = Arc::strong_count(&data);  // 3\n\nlet handle = thread::spawn(move || {\n    println!(\"子线程1：{:?}\", data2);\n    println!(\"子线程1 内计数：{}\", Arc::strong_count(&data2));\n});\n\nlet handle2 = thread::spawn(move || {\n    println!(\"子线程2：{:?}\", data3);\n    println!(\"子线程2 内计数：{}\", Arc::strong_count(&data3));\n});\n\nprintln!(\"主线程：{}\", count);\nhandle.join().unwrap();\nhandle2.join().unwrap();\n```\n\n> `Arc` 只解决了\"多个线程同时拥有一份数据\"。要修改，还得加 `Mutex`。\n\n---\n\n## 4. Arc<Mutex<T>> — 多个线程读写共享数据\n\n### 4.1 10 线程各加 1（最经典模式）\n\n```rust\nuse std::sync::{Arc, Mutex};\n\nlet counter = Arc::new(Mutex::new(0));\nlet mut handles = vec![];\n\nfor _ in 0..10 {\n    let c = Arc::clone(&counter);\n    handles.push(thread::spawn(move || {\n        let mut num = c.lock().unwrap();  // 获取锁\n        *num += 1;                         // 修改\n        // num (MutexGuard) 在此 drop → 锁自动释放\n    }));\n}\n\nfor h in handles {\n    h.join().unwrap();\n}\n\nprintln!(\"结果：{}\", counter.lock().unwrap());  // 10\n```\n\n**三层组合拆解：**\n\n```\nArc<Mutex<i32>>\n │   │    └── 实际数据（整数 0）\n │   └────── 互斥锁：保证同一时刻只有一个人能碰数据\n └────────── 引用计数：保证多线程能共同拥有这把锁\n```\n\n### 4.2 换个起始值，同样套路\n\n```rust\nlet counter = Arc::new(Mutex::new(5));\nlet mut arr = vec![];\n\nfor _ in 0..10 {\n    let c = Arc::clone(&counter);\n    arr.push(thread::spawn(move || {\n        let mut num = c.lock().unwrap();\n        *num += 1;\n    }));\n}\n\nfor i in arr {\n    i.join().unwrap();\n}\nprintln!(\"{:?}\", counter);  // Mutex { data: 15, poisoned: false, .. }\n```\n\n---\n\n## 5. mpsc::channel — 线程间发消息\n\n### 5.1 单发单收\n\n```rust\nuse std::sync::mpsc;\n\nlet (tx, rx) = mpsc::channel();\n\nthread::spawn(move || {\n    let val = String::from(\"子线程的消息\");\n    tx.send(val).unwrap();\n    // val 已移入 channel，这里不能再用\n});\n\nlet received = rx.recv().unwrap();  // 阻塞等待\nprintln!(\"主线程收到：{}\", received);\n```\n\n### 5.2 多发单收（mpsc 的本意）\n\n```rust\nlet (tx, rx) = mpsc::channel();\n\nfor i in 0..5 {\n    let tx_clone = tx.clone();\n    thread::spawn(move || {\n        tx_clone.send(i).unwrap();\n    });\n}\n\ndrop(tx);  // ← 必须 drop 原始 tx，否则 rx 永远等不到结束\n\nfor received in rx {\n    println!(\"收到：{}\", received);\n}\n```\n\n> **为什么必须 `drop(tx)`？** rx 的迭代器只在所有 tx（包括克隆的）都 drop 后才返回 `None`。原始 tx 不用了却不 drop，rx 会永远阻塞。\n\n### 5.3 10 线程发消息求和\n\n```rust\nlet (sx, rx) = mpsc::channel();\n\nfor _ in 0..10 {\n    let sx_clone = sx.clone();\n    thread::spawn(move || {\n        sx_clone.send(1).unwrap();\n    });\n}\ndrop(sx);\n\nlet mut sum = 0;\nfor _ in 0..10 {\n    sum += rx.recv().unwrap();\n}\nprintln!(\"{}\", sum);  // 10\n```\n\n---\n\n## 6. 死锁 — 自己制造然后理解它\n\n```rust\nuse std::sync::{Arc, Mutex};\nuse std::{thread, time::Duration};\n\nlet m1 = Arc::new(Mutex::new(0));\nlet m2 = Arc::new(Mutex::new(0));\n\n// 线程 A：先拿 m1，再拿 m2\nlet a1 = Arc::clone(&m1);\nlet a2 = Arc::clone(&m2);\nlet h1 = thread::spawn(move || {\n    let _g1 = a1.lock().unwrap();\n    println!(\"A 拿到 m1\");\n    thread::sleep(Duration::from_millis(100));\n    let _g2 = a2.lock().unwrap();  // 等 m2 → B 正持有 m2 → 死锁\n    println!(\"A 拿到 m2\");\n});\n\n// 线程 B：先拿 m2，再拿 m1\nlet b1 = Arc::clone(&m1);\nlet b2 = Arc::clone(&m2);\nlet h2 = thread::spawn(move || {\n    let _g2 = b2.lock().unwrap();\n    println!(\"B 拿到 m2\");\n    thread::sleep(Duration::from_millis(100));\n    let _g1 = b1.lock().unwrap();  // 等 m1 → A 正持有 m1 → 死锁\n    println!(\"B 拿到 m1\");\n});\n\nh1.join().unwrap();  // 永远不会返回\nh2.join().unwrap();\n```\n\n**死锁的本质：**\n\n```\n线程 A：持有 m1 → 等待 m2\n                    ✗ 互相等\n线程 B：持有 m2 → 等待 m1\n```\n\n**避免原则：** 所有线程按相同顺序拿锁（如始终先 m1 后 m2）。\n\n---\n\n## 7. AtomicUsize — 简单计数不用锁\n\n```rust\nuse std::sync::atomic::{AtomicUsize, Ordering};\n\nstatic COUNTER: AtomicUsize = AtomicUsize::new(0);\n\nfn main() {\n    let mut handles = vec![];\n    for _ in 0..10 {\n        handles.push(thread::spawn(|| {\n            for _ in 0..1000 {\n                COUNTER.fetch_add(1, Ordering::SeqCst);\n            }\n        }));\n    }\n    for h in handles {\n        h.join().unwrap();\n    }\n    println!(\"结果：{}\", COUNTER.load(Ordering::SeqCst));  // 10000\n}\n```\n\n| 对比       | `Mutex<i32>`                 | `AtomicUsize`       |\n| ---------- | ---------------------------- | ------------------- |\n| 保护范围   | 整个临界区（多条语句）       | 单个整数操作        |\n| 性能       | 有锁开销                     | 无锁，硬件原子指令  |\n| 阻塞       | 会阻塞                       | 不阻塞              |\n| 使用复杂度 | 简单（lock → 改 → 自动释放） | 需要理解 `Ordering` |\n\n> `Ordering::SeqCst` 是最严格的顺序保证，刚开始不用纠结，记住这是\"最安全但最慢\"的选择就行。\n\n---\n\n## 8. 今日总结\n\n### 8.1 并发选型指南\n\n| 需求         | 方案                     |\n| ------------ | ------------------------ |\n| 只读共享     | `Arc<T>`                 |\n| 读写共享     | `Arc<Mutex<T>>`          |\n| 线程间传数据 | `mpsc::channel`          |\n| 简单计数     | `AtomicUsize`            |\n| 启动后台任务 | `thread::spawn` + `join` |\n\n### 8.2 心态记录\n\n之前 Rustlings 做 threads 的时候，是照着注释填空，知其然不知其所以然。今天把每个概念拆成最小单元单独跑：\n\n1. 先玩 `spawn` + `join`（不用共享数据）\n2. 再加 `move`（理解所有权）\n3. 再加 `Arc`（理解多所有者）\n4. 再加 `Mutex`（理解互斥）\n5. 最后组合 `Arc<Mutex<T>>`\n\n分开写清楚每个概念在解决什么问题，组合起来就不觉得神秘了。\n\n### 8.3 明天计划\n\n- 继续练并发，加入错误处理（`lock()` 返回的 `Result` 不只用 `unwrap`）\n- `RwLock` — 多读单写，读多写少场景比 Mutex 性能更好\n- 如果有时间，写一个小的多线程 demo（比如多线程下载器模拟）",
            "url": "https://kyriechao.github.io/posts/2026/08/0/04-rust-day-9",
            "title": "Rust 学习计划",
            "summary": "Rust 第9天学习 — 并发深入练习",
            "date_modified": "2026-08-04T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/0/03-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n学习内容：[C 语言 Day7 笔记](03-rust-day-8.md)——**流程控制结构**\n\n#### 选择结构\n\n| 结构                     | 语法                                    | 适用场景       |\n| ------------------------ | --------------------------------------- | -------------- |\n| `if` 单分支              | `if (表达式) { ... }`                   | 条件满足时执行 |\n| `if-else` 双分支         | `if (表达式) { ... } else { ... }`      | 二选一         |\n| `if-else if-else` 多分支 | 多个 `else if` 级联                     | 多选一         |\n| `switch`                 | `switch (整数) { case 值: ... break; }` | 整数的等值比较 |\n\n- C 语言中**非零即为真**，零为假\n- `switch` 有**穿透（Fall-through）**特性，漏写 `break` 会继续执行后续 case\n- 空语句 `;` 也是合法语句，编译后对应 NOP 指令\n\n#### 循环结构\n\n| 循环                               | 最少执行次数 | 适用场景               |\n| ---------------------------------- | :----------: | ---------------------- |\n| `while (条件) { ... }`             |     0 次     | 循环次数未知           |\n| `do { ... } while (条件);`         |     1 次     | 至少执行一次           |\n| `for (初始化; 条件; 迭代) { ... }` |     0 次     | 循环次数已知（最常用） |\n\n- for 的三个表达式都可留空，但两个分号必须写\n- 精确控制 n 次循环：`for (int i = 0; i < n; i++)`\n- 嵌套不建议超过 3 层\n\n#### 跳转语句\n\n| 语句       | 作用                                 |\n| ---------- | ------------------------------------ |\n| `break`    | 结束 switch 或跳出当前层循环         |\n| `continue` | 跳过本次循环剩余部分，进入下一次迭代 |\n| `goto`     | 无条件跳转到标签处（不推荐使用）     |\n\n#### 输入输出\n\n- `printf(\"格式串\", 变量...)` — 格式化输出\n- `scanf(\"格式串\", &变量...)` — 格式化输入，**必须传地址 `&`**\n\n#### 小工具\n\n今天写了个小脚本 [run.html](https://chaokyrie.asia/run.html)，用于简化单个 C 文件的编译运行流程。\n\n### Rust\n\n今天学的内容不多，主要是**线程部分的巩固复习**。\n\n之前 Rustlings threads 章节已经做完了，但感觉自己还是不太熟，今天重新手写了一遍基础用法：\n\n```rust\nuse std::thread;\nuse std::time::Duration;\n\nfn main() {\n    // join 等待子线程完成\n    let handle = thread::spawn(|| {\n        thread::sleep(Duration::from_secs(3));\n        println!(\"子线程：下载完成!\");\n    });\n    println!(\"主线程：做别的事\");\n    handle.join().unwrap();\n    println!(\"主线程：子线程结束了，我也结束了\");\n\n    // 不 join 的情况：主线程结束时子线程可能还没跑完\n    thread::spawn(|| {\n        thread::sleep(Duration::from_millis(100));\n        println!(\"子线程：完成了!\");\n    });\n    println!(\"主线程：结束了\");\n    // 这里主线程结束，子线程可能来不及执行\n}\n```\n\n复习要点：\n\n- `thread::spawn` 创建新线程，返回 `JoinHandle`\n- `handle.join()` 阻塞主线程，等待子线程完成\n- 不 `join` 的话，主线程结束程序就结束了，子线程可能来不及执行\n- `move` 闭包用于把外部变量所有权移入线程\n\n## 思考\n\n**价值是通过他人评判出来的吗？人的价值只能通过外部评判出来吗？**\n\n我觉得是的。\n\n拿梵高的画来说——他死前根本没人看得上。如果说梵高的画\"本来就有价值\"，那该怪谁？怪世道吗？\n\n他死后才被人看到。人们常说他的价值被\"发现\"了，但\"发现\"这个词不准——因为他死前根本没人看出来，何来发现一说？\n\n换个角度想：假如那时候有人出钱给他办个展，大力宣传一番，有权有势的人来了、捧了，人们自然就觉得他的画有价值了。\n\n所以归根结底，**价值是人自己赋予的**。\n\n如果有人说\"价值本身就在那里，不需要谁来承认\"——这不过是人试图用自己定义的框架，去框住一个本不属于它的名字罢了。\n\n## 明日计划\n\n1. Rust：继续线程部分深入学习——`mpsc::channel` 消息传递、`Arc<Mutex<T>>` 共享内存模式",
            "url": "https://kyriechao.github.io/posts/2026/08/0/03-diary",
            "title": "开始学习",
            "summary": "C 语言流程控制结构，Rust 线程巩固复习",
            "date_modified": "2026-08-03T13:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/0/03-rust-day-8",
            "content_html": "\n## Day 8 目标\n\nRustlings 做完了，但并发和 trait 两块还是不够熟。Day 8 集中补这两个方向。\n\n---\n\n## 一、线程与并发深化\n\n### 1.1 `mpsc::channel` —— 消息传递并发\n\nGo 的那句\"用通信来共享内存\"，Rust 同样支持。`mpsc` 代表 **m**ultiple **p**roducer, **s**ingle **c**onsumer。\n\n```rust\nuse std::sync::mpsc;\nuse std::thread;\n\nfn main() {\n    let (tx, rx) = mpsc::channel();\n\n    let tx1 = tx.clone();\n    thread::spawn(move || {\n        tx1.send(\"来自线程1\").unwrap();\n    });\n\n    let tx2 = tx.clone();\n    thread::spawn(move || {\n        tx2.send(\"来自线程2\").unwrap();\n    });\n\n    drop(tx);  // 原始 tx 必须 drop，否则 rx 会永远等待\n\n    for msg in rx {\n        println!(\"{}\", msg);\n    }\n}\n```\n\n要点：\n\n- `tx` 克隆后分发给各线程，`rx` 作为迭代器收消息\n- **必须 drop 原始 tx**，否则接收端不知道发送者何时全部结束\n- `send` 返回 `Result`——接收端已 drop 时会报错\n\n### 1.2 `Arc<Mutex<T>>` —— 共享内存并发\n\n```rust\nuse std::sync::{Arc, Mutex};\nuse std::thread;\n\nlet counter = Arc::new(Mutex::new(0));\nlet mut handles = vec![];\n\nfor _ in 0..10 {\n    let c = Arc::clone(&counter);\n    handles.push(thread::spawn(move || {\n        let mut num = c.lock().unwrap();\n        *num += 1;\n    }));\n}\n\nfor h in handles {\n    h.join().unwrap();\n}\n\nprintln!(\"{}\", *counter.lock().unwrap());  // 10\n```\n\n每一步的含义：\n\n| 步骤     | 代码                      | 作用                                                 |\n| -------- | ------------------------- | ---------------------------------------------------- |\n| ① 创建   | `Arc::new(Mutex::new(0))` | 把数据包在 Mutex 里，再包在 Arc 里                   |\n| ② 克隆   | `Arc::clone(&counter)`    | 每个线程拿一份 Arc 的拷贝（引用计数 +1，不拷贝数据） |\n| ③ 移入   | `move || { ... }`         | 强制闭包获取 Arc 的所有权                            |\n| ④ 加锁   | `c.lock().unwrap()`       | 获取 MutexGuard，阻塞直到拿到锁                      |\n| ⑤ 修改   | `*num += 1`               | 解引用 MutexGuard 后修改内部数据                     |\n| ⑥ 等线程 | `h.join()`                | 主线程等待所有子线程完成                             |\n\n### 1.3 锁中毒（Poisoning）\n\n如果线程在持有锁时 panic，锁会\"中毒\"——后续 `lock()` 返回 `Err(PoisonError)`。\n\n```rust\n// 简单粗暴：中毒也 panic\n*counter.lock().unwrap() += 1;\n\n// 更稳健：中毒后仍能恢复数据\nmatch counter.lock() {\n    Ok(mut guard) => *guard += 1,\n    Err(poisoned) => *poisoned.into_inner() += 1,\n}\n```\n\n### 1.4 其他并发原语速览\n\n| 原语                   | 一句话                           | 类比                            |\n| ---------------------- | -------------------------------- | ------------------------------- |\n| `RwLock<T>`            | 多读单写锁                       | 读写锁，读不互斥、写互斥        |\n| `Barrier`              | 多个线程在某个点汇合             | 等人齐了一起走                  |\n| `Condvar`              | 条件变量，配合 Mutex 做等待/通知 | `while !ready { wait() }`       |\n| `OnceLock`             | 只写入一次、之后只读的单值容器   | 线程安全的 lazy static          |\n| `AtomicBool/I32/Usize` | 无锁原子操作                     | 简单的计数器/标志位不需要 Mutex |\n\n> 先熟练掌握 `channel` 和 `Arc<Mutex<T>>` 两个模式，够覆盖 80% 的并发场景。其余用到时再查。\n\n---\n\n## 二、Trait —— 共享行为的抽象\n\n### 2.1 基本语法\n\n```rust\n// 定义 trait\ntrait Summary {\n    fn summarize(&self) -> String;\n\n    // 默认实现\n    fn summarize_author(&self) -> String {\n        String::from(\"(未知作者)\")\n    }\n}\n\n// 为类型实现 trait\nstruct Article {\n    title: String,\n    author: String,\n    content: String,\n}\n\nimpl Summary for Article {\n    fn summarize(&self) -> String {\n        format!(\"{} —— {}\", self.title, self.author)\n    }\n}\n\n// 调用\nlet a = Article { ... };\nprintln!(\"{}\", a.summarize());\n```\n\n> `impl Trait for Type` —— trait 和类型至少有一个必须在本 crate 中定义（**孤儿规则**）。你不能为外部类型实现外部 trait。\n\n### 2.2 Trait 作为参数\n\n```rust\n// 写法一：impl Trait 语法糖\nfn notify(item: &impl Summary) {\n    println!(\"{}\", item.summarize());\n}\n\n// 写法二：trait bound（等价，更显式）\nfn notify<T: Summary>(item: &T) {\n    println!(\"{}\", item.summarize());\n}\n\n// 多个 trait bound\nfn notify<T: Summary + Display>(item: &T) { ... }\n\n// where 子句（参数多、约束多时更可读）\nfn some_function<T, U>(t: &T, u: &U) -> i32\nwhere\n    T: Display + Clone,\n    U: Clone + Debug,\n{ ... }\n```\n\n### 2.3 返回实现了 Trait 的类型\n\n```rust\nfn returns_summarizable() -> impl Summary {\n    Article { ... }\n}\n```\n\n> `impl Trait` 作返回类型时，函数只能返回**同一种类型**。不能在某些分支返回 `Article`、另一些分支返回 `Tweet`。\n\n### 2.4 常用标准库 Trait\n\n| Trait           | 用途                   | 获取方式                         |\n| --------------- | ---------------------- | -------------------------------- |\n| `Debug`         | 调试打印 `{:?}`        | `#[derive(Debug)]`               |\n| `Clone`         | 显式深拷贝 `.clone()`  | `#[derive(Clone)]`               |\n| `Copy`          | 隐式按位拷贝（栈数据） | `#[derive(Copy, Clone)]`         |\n| `PartialEq`     | 相等比较 `==` `!=`     | `#[derive(PartialEq)]`           |\n| `Default`       | 默认值                 | `#[derive(Default)]`             |\n| `Display`       | 面向用户的格式化 `{}`  | **手动实现**                     |\n| `Drop`          | 离开作用域时执行的清理 | 手动实现 `fn drop(&mut self)`    |\n| `From` / `Into` | 类型转换               | 手动实现 `From`，`Into` 自动获得 |\n| `Iterator`      | 迭代器                 | 手动实现 `fn next(&mut self)`    |\n\n### 2.5 练习——写一个自定义 trait\n\n试着不用 `#[derive]`，手动为你的 `Rectangle` 实现 `PartialEq` 和 `Display`：\n\n```rust\nstruct Rectangle {\n    width: u32,\n    height: u32,\n}\n\n// 手动实现 PartialEq\nimpl PartialEq for Rectangle {\n    fn eq(&self, other: &Self) -> bool {\n        self.width == other.width && self.height == other.height\n    }\n}\n\n// 手动实现 Display\nimpl std::fmt::Display for Rectangle {\n    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n        write!(f, \"Rectangle({} x {})\", self.width, self.height)\n    }\n}\n```\n\n---\n\n## 三、今日练习清单\n\n| #    | 练习                                                         | 重点               |\n| ---- | ------------------------------------------------------------ | ------------------ |\n| 1    | 用 `mpsc::channel` 实现：3 个线程各产生 10 个随机数，汇总到主线程求平均值 | channel 消息传递   |\n| 2    | 用 `Arc<Mutex<T>>` 实现：5 个线程同时对同一个 `Vec<i32>` 追加元素，最后打印完整列表 | 共享内存 + 锁      |\n| 3    | 不用 channel、不用共享内存，只用 `thread::spawn` + `join` 实现：4 个线程各自计算，最后汇总 | 多线程返回值       |\n| 4    | 为你的 `Rectangle` 手动实现 `PartialEq`、`Display`、`From<(u32, u32)>` | Trait 实现套路     |\n| 5    | 写一个函数 `largest<T: PartialOrd>(list: &[T]) -> &T`，找出切片中最大元素的引用 | 泛型 + trait bound |\n\n---\n\n## 四、Day 8 检查点\n\n- [ ] 能用 `channel` 在多线程间传递消息\n- [ ] 能用 `Arc<Mutex<T>>` 安全共享可变数据\n- [ ] 理解锁中毒的机制和处理方式\n- [ ] 能写出带 trait bound 的泛型函数\n- [ ] 能手写 `impl Display for Xxx`（不用 derive）\n- [ ] 理解孤儿规则的限制\n\n---\n\n## 五、如果还有时间——再看一眼生命周期\n\n之前 `longest` 能写对，但复杂场景还是会卡。试一下这个结构体标注：\n\n```rust\nstruct Excerpt<'a> {\n    part: &'a str,  // 要求：Excerpt 实例不能比它引用的 str 活得更久\n}\n\nimpl<'a> Excerpt<'a> {\n    // 省略规则自动处理，无需显式标注\n    fn announce(&self, msg: &str) -> &str {\n        println!(\"{}\", msg);\n        self.part\n    }\n}\n```\n\n规则回顾——三个省略规则，当编译器能自动推断时就不用写：\n\n1. 每个引用参数都有各自的生命周期\n2. 只有一个输入生命周期 → 赋予所有输出\n3. `&self` / `&mut self` → self 的生命周期赋予所有输出\n\n只有当三条规则都不满足时，才需要手动标注。",
            "url": "https://kyriechao.github.io/posts/2026/08/0/03-rust-day-8",
            "title": "Rust 学习计划",
            "summary": "Rust 第8天学习",
            "date_modified": "2026-08-03T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/0/01-diary",
            "content_html": "\n## 技术\n\n### C 语言\n\n今天没有做 C 语言编程练习，但补充了一个关于隐式类型转换的知识点。\n\n#### 整数类型比较规则（C 语言隐式转换）\n\n当两个不同类型的整数做比较或运算时，编译器会按以下规则做隐式类型转换：\n\n**1. 整形提升（Integer Promotion）**\n\n比 `int` 小的类型（`char`、`short`），运算前会先统一提升为 `int`（有符号）再参与运算。\n\n```c\nchar a = 10;\nshort b = 20;\n// a + b 运算时，a 和 b 都会先被提升为 int\n```\n\n**2. 同级整数的符号处理**\n\n和 `int` 同级的类型比较时，**无符号比有符号\"大\"**——有符号数会被强制转换为无符号数再比较。\n\n```c\nint a = -1;\nunsigned int b = 1;\n// a > b ？不，-1 被转成无符号后变成一个极大的正数（UINT_MAX），所以 a > b\n```\n\n> 这是 C 语言里一个常见的坑：有符号和无符号混用时，结果往往违反直觉。\n\n**3. 浮点与整型混用**\n\n等级排序：`long double` > `double` > `float` > 整型（`long long` > `long` > `int` > `short` > `char`）\n\n规则：\n\n- **等级低的向等级高的转**\n- 同等级整数内，**无符号吃掉有符号**\n\n```c\nint i = 5;\ndouble d = 3.14;\n// i + d → i 先转为 double，结果为 double 类型\n\nint si = -10;\nunsigned int ui = 5;\n// si + ui → si 先转为 unsigned int，结果可能很大\n```\n\n### Rust\n\n今天完成了 Rustlings 全部练习，看到 Congratulations 的 Finish line。\n\n具体内容记录在 [Rust Day7 笔记](01-rust-day7.md)，要点：\n\n- **生命周期标注**：手写了带生命周期的 `Book<'a>` 结构体及 `impl` 块，能写 `longer_title` 这类多引用返回的方法\n- **闭包**：`Fn` / `FnMut` / `FnOnce` 三个 trait 的区别和适用场景\n- **迭代器**：`filter` + `map` + `collect` 链式调用、`fold`、`zip`、`next`\n- **智能指针**：`Box`（递归类型）、`Rc`（共享所有权）、`RefCell`（内部可变性）、`Arc`（多线程共享）\n- **并发**：`Arc<Mutex<T>>` 模式、`thread::spawn` + `join`、`mpsc::channel` 消息传递\n- **宏**：声明宏基础语法（`macro_rules!`），四种宏类型对比\n- **Clippy**：常用 lint 规则和处理方式\n- **类型转换**：`From` / `TryFrom` / `FromStr` / `AsRef` 的实现套路\n\n能力自评：所有权/借用基本内化，迭代器方法库是目前的短板，过程宏还没碰。\n\n## 思考\n\n（今天没有特别的思考内容）\n\n## 明日计划\n\n1. 继续 Rust 后续课程（Trait / 泛型约束深入，如果课程还没到则自己看 Rust Book §10）\n2. 精读 Iterator trait 文档，扩充迭代器方法储备",
            "url": "https://kyriechao.github.io/posts/2026/08/0/01-diary",
            "title": "周六放松",
            "summary": "C 隐式类型转换规则补充，Rustlings 完结",
            "date_modified": "2026-08-01T13:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/08/0/01-rust-day7",
            "content_html": "\n# Rust Day7 — Rustlings 完结 & 综合练习\n\nRustlings 全部做完，看到 Fe-nish line 那一刻还是挺有成就感的。\n\n但说实话，我只是**知道了几个函数怎么用**，离\"完全掌握\"还有距离。今天把薄弱环节集中补一下。\n\n---\n\n## 1. 今日手写代码\n\n### 1.1 生命周期（Lifetimes）\n\n```rust\n#[derive(Debug)]\nenum List {\n    Cons(i32, Box<List>),\n    Nil,\n}\n\nimpl List {\n    fn len(&self) -> i32 {\n        match self {\n            List::Cons(_, next) => 1 + next.len(),\n            List::Nil => 0,\n        }\n    }\n}\n\nstruct Book<'a> {\n    title: &'a str,\n    author: &'a str,\n}\n\nimpl<'a> Book<'a> {\n    fn get_title(&self) -> &str {\n        self.title\n    }\n\n    fn longer_title<'b>(&self, other: &'a str) -> &'a str {\n        if self.title.len() > other.len() {\n            self.title\n        } else {\n            other\n        }\n    }\n}\n```\n\n**生命周期知识点：**\n\n| 概念                | 说明                                                         |\n| ------------------- | ------------------------------------------------------------ |\n| `'a` 语法           | 生命周期参数，标注引用\"活多久\"。`&'a str` 表示这个引用的有效期至少为 `'a` |\n| struct 中的生命周期 | 当 struct 持有引用时，必须标注生命周期：编译器需要保证 struct 实例活得比引用短 |\n| impl 中的生命周期   | `impl<'a> Book<'a>` — 在 impl 后声明泛型生命周期，在类型后使用 |\n| 省略规则            | `fn get_title(&self) -> &str` 编译器能自动推断，不用显式写   |\n| 多引用返回          | `longer_title` 返回的引用可能来自 `self.title` 或 `other`，生命期标注告诉编译器\"返回值的生命周期等于较短的那个\" |\n\n> **核心直觉：** 生命周期标注不改变代码逻辑，只是在给编译器\"画约束线\"——\"这个引用不能比那个引用活得久\"。标注完后编译器帮你检查是否违反了约束。\n\n### 1.2 闭包（Closures）\n\n```rust\nfn main() {\n    // 不可变借用 —— 实现 Fn\n    let base = 10;\n    let add_base = |n| base + n;\n    println!(\"{}\", add_base(5));  // 15\n\n    // 可变借用 —— 实现 FnMut\n    let mut count = 0;\n    let mut counter = || {\n        count += 1;\n        count\n    };\n    println!(\"{}\", counter());  // 1\n    println!(\"{}\", counter());  // 2\n\n    // 闭包作为参数 —— Fn trait bound\n    fn apply_twice<F>(f: F, n: i32) -> i32\n    where\n        F: Fn(i32) -> i32,\n    {\n        f(f(n))\n    }\n    println!(\"{}\", apply_twice(|n| n * 2, 5));  // 20\n}\n```\n\n| trait    | 调用方式    | 捕获方式   | 能多次调用    |\n| -------- | ----------- | ---------- | ------------- |\n| `Fn`     | `&self`     | 不可变借用 | ✓             |\n| `FnMut`  | `&mut self` | 可变借用   | ✓             |\n| `FnOnce` | `self`      | 转移所有权 | ✗（只能一次） |\n\n### 1.3 迭代器（Iterators）\n\n```rust\nlet nums = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n\n// filter + map + collect\nlet result: Vec<i32> = nums\n    .iter()\n    .filter(|&&x| x % 2 == 0)\n    .map(|&x| x * 3)\n    .collect();\nprintln!(\"{:?}\", result);  // [6, 12, 18, 24, 30]\n\n// fold 累积\nlet product = nums.iter().fold(1, |acc, &x| acc * x);\nprintln!(\"{}\", product);  // 3628800\n\n// zip 并行迭代\nlet names = vec![\"Tom\", \"Jerry\", \"Spike\"];\nlet scores = vec![80, 95, 60];\nfor (name, score) in names.iter().zip(scores.iter()) {\n    println!(\"{}:{}\", name, score);\n}\n\n// 链式查找：第一个大于5的偶数，乘2\nlet first = nums\n    .iter()\n    .filter(|&&x| x % 2 == 0 && x > 5)\n    .map(|&o| o * 2)\n    .next()\n    .unwrap_or(0);\nprintln!(\"{}\", first);  // 12\n```\n\n### 1.4 智能指针（Smart Pointers）\n\n```rust\nuse std::{\n    cell::RefCell,\n    rc::Rc,\n};\n\n// Rc<Vec<i32>> — 共享所有权\nlet v = Rc::new(vec![1, 2, 3]);\nlet v2 = Rc::clone(&v);\nlet v3 = Rc::clone(&v);\nprintln!(\"{}\", Rc::strong_count(&v));  // 3\n\n// Rc<RefCell<i32>> — 共享 + 内部可变\nlet num = Rc::new(RefCell::new(5));\nlet num2 = Rc::clone(&num);\n*num.borrow_mut() += 1;    // 5 + 1 = 6\n*num2.borrow_mut() *= 2;   // 6 * 2 = 12\nprintln!(\"{}\", num.borrow());  // 12\n\n// Box 实现递归链表\n#[derive(Debug)]\nenum List {\n    Cons(i32, Box<List>),\n    Nil,\n}\n\nlet list = Box::new(List::Cons(\n    1,\n    Box::new(List::Cons(2, Box::new(List::Cons(3, Box::new(List::Nil))))),\n));\nprintln!(\"{}\", list.len());  // 3\n```\n\n**三种智能指针对比：**\n\n| 智能指针     | 用途             | 线程安全 | 等价于                    |\n| ------------ | ---------------- | -------- | ------------------------- |\n| `Box<T>`     | 堆分配，递归类型 | —        | C 的 `malloc`（自动释放） |\n| `Rc<T>`      | 单线程共享所有权 | ❌        | 非原子的引用计数          |\n| `Arc<T>`     | 多线程共享所有权 | ✓        | 原子的引用计数            |\n| `RefCell<T>` | 运行时借用检查   | ❌        | 编译期检查的\"后门\"        |\n| `Mutex<T>`   | 多线程互斥访问   | ✓        | 互斥锁                    |\n| `Cow<T>`     | 写时克隆         | —        | 懒克隆优化                |\n\n### 1.5 并发（Concurrency）\n\n```rust\nuse std::{\n    sync::{Arc, Mutex},\n    thread,\n};\n\n// 10 个线程共享一个计数器，每个 +1\nlet counter = Arc::new(Mutex::new(0));\nlet mut handles = vec![];\n\nfor _ in 0..10 {\n    let c = Arc::clone(&counter);\n    handles.push(thread::spawn(move || {\n        *c.lock().unwrap() += 1;\n    }));\n}\n\nfor h in handles {\n    h.join().unwrap();\n}\nprintln!(\"{:?}\", counter.lock().unwrap());  // 10\n```\n\n**并发三板斧：**\n\n```\nArc<Mutex<T>>\n  │   └── 互斥锁：保证同一时刻只有一个线程能修改数据\n  └────── 原子引用计数：保证多线程共享所有权（Rc 不行！）\n```\n\n| 组件            | 作用                                                    |\n| --------------- | ------------------------------------------------------- |\n| `thread::spawn` | 创建新线程，返回 `JoinHandle`                           |\n| `move` 闭包     | 强制把外部变量所有权移入线程闭包                        |\n| `Arc::clone`    | 给每个线程一份\"共享指针的副本\"（不拷贝数据）            |\n| `Mutex::lock()` | 获取锁，返回 `LockResult<MutexGuard<T>>`                |\n| `unwrap()`      | 简化错误处理——若其他线程 panic 导致锁中毒，这里也 panic |\n| `join()`        | 主线程等待子线程完成                                    |\n\n---\n\n## 2. Rustlings 19~23 练习总结\n\n### 2.1 19_smart_pointers — 智能指针\n\n| 练习题    | 涉及内容              | 关键收获                                                     |\n| --------- | --------------------- | ------------------------------------------------------------ |\n| `box1.rs` | `Box<T>` 解决递归类型 | Rust 需要在编译期知道类型大小，`Box` 把递归部分放堆上，因为是固定大小的指针 |\n| `rc1.rs`  | `Rc<T>` 共享所有权    | 太阳系模型：一颗 Sun，8 颗行星通过 `Rc::clone` 共享太阳。`strong_count` 增减可视化 |\n| `arc1.rs` | `Arc<T>` 多线程共享   | `Rc` 不实现 `Send`，多线程必须用 `Arc`。8 个线程各持有 `Arc::clone` |\n| `cow1.rs` | `Cow<T>` 写时克隆     | `Cow::Borrowed` 不拷贝；`to_mut()` 触发克隆变 `Cow::Owned`。只有需要修改才拷贝 |\n\n### 2.2 20_threads — 线程\n\n| 练习题        | 涉及内容                 | 关键收获                                                     |\n| ------------- | ------------------------ | ------------------------------------------------------------ |\n| `threads1.rs` | `thread::spawn` + `join` | `JoinHandle.join().unwrap()` 等待线程结束并取返回值          |\n| `threads2.rs` | `Arc<Mutex<T>>`          | `lock().unwrap()` 获取 `MutexGuard`，解引用后修改。离开作用域自动释放锁 |\n| `threads3.rs` | `mpsc::channel`          | 消息传递并发。多生产者单消费者：`tx.clone()` 克隆发送端，`rx` 收所有消息 |\n\n> **为什么需要 Mutex？** 如果多个线程直接 `+=1`，会有数据竞争（data race）——读-改-写不是原子操作。Mutex 保证\"锁住 → 修改 → 释放\"这个序列是互斥的。\n\n### 2.3 21_macros — 宏\n\n| 练习题       | 涉及内容          | 关键收获                                                     |\n| ------------ | ----------------- | ------------------------------------------------------------ |\n| `macros1.rs` | 宏调用语法        | `my_macro!()` — 声明宏调用必须加 `!`                         |\n| `macros2.rs` | 宏定义顺序        | 宏必须在调用**之前**定义（与函数不同）                       |\n| `macros3.rs` | `#[macro_export]` | 模块内的宏默认私有，加 `#[macro_export]` 导出到 crate root   |\n| `macros4.rs` | 宏重载            | 同一个宏名可定义多个匹配分支：无参 `()` 和带参 `($val:expr)` |\n\n**声明宏基础语法：**\n\n```rust\nmacro_rules! 宏名 {\n    (模式1) => { 展开1 };\n    (模式2) => { 展开2 };\n}\n\n// 模式中常用设计符：\n// $var:expr   — 表达式\n// $var:ty     — 类型\n// $var:ident  — 标识符\n// $var:literal — 字面量\n// $var:tt     — 单个 token tree（最通用）\n```\n\n> 说实话 macros 这四道题太简单了，只碰了声明宏的皮毛。过程宏（`#[proc_macro]`）、`#[derive]` 宏、属性宏才是真正强大的部分，但 Rustlings 没涉及。\n\n### 2.4 22_clippy — 代码规范 Lint\n\n| 练习题       | Clippy 规则            | 修正方法                                                     |\n| ------------ | ---------------------- | ------------------------------------------------------------ |\n| `clippy1.rs` | `approx_constant`      | `3.14` → `std::f32::consts::PI`                              |\n| `clippy2.rs` | `if_let` 替代 `unwrap` | `option.unwrap()` → `if let Some(x) = option { ... }`        |\n| `clippy3.rs` | 多个 lint 综合         | `if let` 替代 `unwrap`；`swap` 替代手动交换；`vec.resize` 替代 `vec![..].resize(0,..)` 等 |\n\n**常用 Clippy 规则速查：**\n\n| Lint 名                   | 检测内容                                                     |\n| ------------------------- | ------------------------------------------------------------ |\n| `clippy::unwrap_used`     | 禁止 `unwrap()`（生产代码推荐用 `?` 或 `expect`）            |\n| `clippy::needless_return` | 函数末尾多余的 `return`                                      |\n| `clippy::if_let`          | 能用 `if let` 的地方不要 `match` 单分支                      |\n| `clippy::approx_constant` | 硬编码的数学常量（如 `3.14` 应替换为 `std::f32::consts::PI`） |\n| `clippy::manual_swap`     | 手动三行交换两个变量，应用 `std::mem::swap`                  |\n\n> Clippy 就像 Rust 的\"代码规范老师\"——它不光告诉你哪里不对，还告诉你为什么以及怎么改。生产项目建议配 `cargo clippy -- -D warnings`，把所有警告当错误处理。\n\n### 2.5 23_conversions — 类型转换\n\n| 练习题             | 涉及 trait            | 关键收获                                                     |\n| ------------------ | --------------------- | ------------------------------------------------------------ |\n| `using_as.rs`      | `as` 关键字           | `values.len() as f64` — `as` 做基本类型转换。注意：`as` 不检查溢出 |\n| `from_into.rs`     | `From` + `Into`       | 实现 `From<&str> for Person`，`Into` 自动获得。失败时 fallback 到 `Default` |\n| `from_str.rs`      | `FromStr`             | 与 `From` 不同：返回 `Result`，失败带错误信息。实现后可直接 `.parse::<Person>()` |\n| `try_from_into.rs` | `TryFrom` + `TryInto` | 可能失败的转换。实现了 tuple、数组、slice 三种输入版本的 `TryFrom` |\n| `as_ref_mut.rs`    | `AsRef` + `AsMut`     | `AsRef<str>` 让函数同时接受 `&str` 和 `String`。`AsMut` 同理用于可变引用 |\n\n**转换 trait 选择指南：**\n\n```\n值到值的转换：\n  ┌─ 不会失败 ──→ 实现 From\n  └─ 可能失败 ──→ 实现 TryFrom（自动得 TryInto）\n\n字符串解析：\n  └── 可能失败 ──→ 实现 FromStr（自动得 .parse()）\n\n引用到引用的转换（零成本）：\n  └── 实现 AsRef / AsMut\n\n简单数值转换（不检查）：\n  └── 用 as 关键字\n```\n\n> 老实说 conversions 这几题主要就是反复练习 trait 实现的套路，没有特别新的概念。`FromStr` vs `From` 的区别值得记住：一个返回 `Result`，一个返回 `Self`。\n\n---\n\n## 3. 薄弱环节深入\n\n### 3.1 线程与并发 — 补充理解\n\n**`Arc<Mutex<T>>` 模式的工作流程：**\n\n```rust\nuse std::sync::{Arc, Mutex};\nuse std::thread;\n\nlet data = Arc::new(Mutex::new(0));     // ① 创建共享数据\n\nlet data_clone = Arc::clone(&data);     // ② 克隆 Arc（引用计数 +1）\nthread::spawn(move || {                 // ③ move 移入闭包\n    let mut guard = data_clone.lock().unwrap();  // ④ 获取锁\n    *guard += 1;                        // ⑤ 修改数据\n});                                     // ⑥ guard drop → 自动释放锁\n```\n\n**Mutex 锁中毒（Poisoning）：**\n\n```rust\n// 如果一个线程在持有锁时 panic，锁会\"中毒\"\n// lock() 的返回值是 LockResult<MutexGuard<T>>\n// 中毒时 lock() 返回 Err(PoisonError)\n\n// 简单处理：unwrap() — 中毒也 panic\n*counter.lock().unwrap() += 1;\n\n// 更安全的处理：\nmatch counter.lock() {\n    Ok(mut guard) => *guard += 1,\n    Err(poisoned) => {\n        // 锁已中毒，但仍可通过 into_inner() 恢复数据\n        *poisoned.into_inner() += 1;\n    }\n}\n```\n\n**`mpsc::channel`（消息传递并发）：**\n\n```rust\nuse std::sync::mpsc;\nuse std::thread;\n\nlet (tx, rx) = mpsc::channel();        // tx = 发送端, rx = 接收端\n\nlet tx1 = tx.clone();                   // 多个发送者\nthread::spawn(move || {\n    tx1.send(\"来自线程1\").unwrap();     // 发送消息\n});\n\nlet tx2 = tx.clone();\nthread::spawn(move || {\n    tx2.send(\"来自线程2\").unwrap();\n});\n\ndrop(tx);                               // 原始 tx 也要 drop，否则 rx 会永远等待\n\nfor msg in rx {                         // rx 作为迭代器，直到所有 tx 都 drop\n    println!(\"{}\", msg);\n}\n```\n\n> **Go 的哲学是\"用通信来共享内存\"**，Rust 同时支持两种范式：channel（消息传递）和 `Arc<Mutex<T>>`（共享内存）。Mutex 更直观但容易死锁，channel 更安全但有时不方便。\n\n### 3.2 Rc + RefCell 的内部原理\n\n```rust\nuse std::{cell::RefCell, rc::Rc};\n\n// Rc 解决 \"谁拥有这块数据\" — 引用计数\n// RefCell 解决 \"不可变引用下怎么修改\" — 运行时借用检查\nlet shared = Rc::new(RefCell::new(vec![1, 2, 3]));\n\n{\n    let mut v = shared.borrow_mut();    // 运行时检查：有没有其他借用？\n    v.push(4);                          // 可变借用，修改数据\n}                                       // v drop → 可变借用释放\n\nlet v2 = shared.borrow();              // 不可变借用，读取数据\nprintln!(\"{:?}\", v2);                  // [1, 2, 3, 4]\n```\n\n**RefCell 的运行时检查规则（与编译期规则完全对应）：**\n\n| 操作           | 成功条件                       | 失败结果 |\n| -------------- | ------------------------------ | -------- |\n| `borrow()`     | 当前没有 `borrow_mut()` 持有中 | `panic!` |\n| `borrow_mut()` | 当前没有任何 borrow（读或写）  | `panic!` |\n\n> **为什么需要 RefCell？** 编译期借用检查是保守的——有时你明知逻辑上安全，但编译器不买账。RefCell 把检查推迟到运行时，给你更大的灵活性，代价是写错会 panic 而不是编译报错。\n\n### 3.3 宏 — 声明宏进阶\n\nRustlings 教到的最远的地方是多分支宏。补充几个实际场景：\n\n```rust\n// vec! 宏的实际实现思路（简化版）\nmacro_rules! my_vec {\n    () => { Vec::new() };\n    ($elem:expr; $n:expr) => {              // vec![0; 10] → 10 个 0\n        std::vec::from_elem($elem, $n)\n    };\n    ($($x:expr),+ $(,)?) => {               // vec![1, 2, 3] → 可变参数\n        {\n            let mut v = Vec::new();\n            $(v.push($x);)+\n            v\n        }\n    };\n}\n\n// 重复模式语法：\n// $($var:expr),*     — 零个或多个，逗号分隔\n// $($var:expr),+     — 一个或多个，逗号分隔\n// $(,)?              — 可选的尾随逗号\n```\n\n四种宏类型的对比：\n\n| 宏类型   | 定义方式                  | 使用方式           | 典型场景           |\n| -------- | ------------------------- | ------------------ | ------------------ |\n| 声明宏   | `macro_rules!`            | `macro!()`         | `println!`、`vec!` |\n| 派生宏   | `#[proc_macro_derive]`    | `#[derive(Debug)]` | 自动实现 trait     |\n| 属性宏   | `#[proc_macro_attribute]` | `#[route(\"GET\")]`  | 代码标注/转换      |\n| 函数式宏 | `#[proc_macro]`           | `sql!(SELECT ...)` | 自定义语法         |\n\n> Rustlings 只教了声明宏的入门，剩下三种过程宏需要单独深入。\n\n---\n\n## 4. Rustlings 全阶段学习反思\n\n### 4.1 能力自评\n\n| 知识模块                     | 自评         | 说明                                                         |\n| ---------------------------- | ------------ | ------------------------------------------------------------ |\n| 所有权 / 借用 / 生命周期     | 基本会用     | 生命周期标注遇到复杂场景还是会卡                             |\n| 枚举 / 模式匹配              | 熟练         | `match`、`if let`、`Option`/`Result` 操作顺手                |\n| Vec / String / HashMap       | 会用         | 常用方法熟悉，Entry API 是亮点                               |\n| 闭包                         | 基本会用     | `Fn`/`FnMut`/`FnOnce` 区别理解，但写 trait bound 时偶尔会忘  |\n| 迭代器                       | 会用但不熟   | 上百个方法只记住了常用的十几个，遇到新需求经常想不出该用哪个 |\n| `Box` / `Rc` / `RefCell`     | 会用         | 知道各自用途和限制，组合使用没问题                           |\n| `Arc` / `Mutex` / 线程       | 了解         | 能写简单多线程程序，但并发模式的积累不够                     |\n| `From` / `TryFrom` / `AsRef` | 会用         | 懂了套路，缺的是多练                                         |\n| 宏                           | 能读懂不会写 | 声明宏基础语法会了，过程宏还没碰                             |\n| 错误处理                     | 会用         | `Result` + `?` 很顺手，自定义错误类型也练过                  |\n| 测试                         | 会用         | `#[test]` + `assert_eq!` 基础够用                            |\n\n### 4.2 下一步学习方向\n\n1. **迭代器方法库**：精读 Iterator trait 文档，记住 20~30 个常用方法\n2. **并发深入**：`mpsc`、`RwLock`、`Barrier`、`Condvar`、`OnceLock` 等更多并发原语\n3. **过程宏**：尝试写一个 `#[derive]` 宏，理解 `syn` + `quote` 工作流\n4. **生命周期实战**：多写几个需要显式标注生命周期签名的复杂 struct/fn\n5. **实际项目**：用 Rust 写一个小项目（CLI 工具或嵌入式驱动），把碎片知识串起来\n\n---\n\n## 5. Rustlings 全部章节对照表\n\n| 练习            | 《Rust Book》章节 | 完成 |\n| --------------- | ----------------- | ---- |\n| variables       | §3.1              | ✓    |\n| functions       | §3.3              | ✓    |\n| if              | §3.5              | ✓    |\n| primitive_types | §3.2, §4.3        | ✓    |\n| vecs            | §8.1              | ✓    |\n| move_semantics  | §4.1-2            | ✓    |\n| structs         | §5.1, §5.3        | ✓    |\n| enums           | §6, §18.3         | ✓    |\n| strings         | §8.2              | ✓    |\n| modules         | §7                | ✓    |\n| hashmaps        | §8.3              | ✓    |\n| options         | §10.1             | ✓    |\n| error_handling  | §9                | ✓    |\n| generics        | §10               | ✓    |\n| traits          | §10.2             | ✓    |\n| lifetimes       | §10.3             | ✓    |\n| tests           | §11.1             | ✓    |\n| iterators       | §13.2-4           | ✓    |\n| smart_pointers  | §15, §16.3        | ✓    |\n| threads         | §16.1-3           | ✓    |\n| macros          | §20.5             | ✓    |\n| clippy          | Appendix D        | ✓    |\n| conversions     | n/a               | ✓    |\n\n---\n\n## 6. 备忘\n\n- `as` 转换不检查溢出，生产代码中跨类型窄化转换建议用 `TryFrom` 或用 `.try_into()` 后处理 `Result`\n- `Rc` 不能跨线程（不实现 `Send`），改用 `Arc`；`RefCell` 也不能跨线程（不实现 `Sync`），改用 `Mutex` 或 `RwLock`\n- `Mutex::lock()` 会阻塞当前线程直到获取锁；`try_lock()` 不阻塞，获取不到马上返回 `Err`\n- Clippy 默认只 warn，配 `-D warnings` 才能让 lint 阻止编译",
            "url": "https://kyriechao.github.io/posts/2026/08/0/01-rust-day7",
            "title": "Rust 学习计划",
            "summary": "Rust 第7天学习 — Rustlings 完结",
            "date_modified": "2026-08-01T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/31-diary",
            "content_html": "\n## 技术\n\n### C 语言\n今天讲了三块内容：\n\n- **位运算符**（`&` `|` `^` `~` `<<` `>>`）—\n  清零、置一、翻转、移位，以及用掩码操作指定位的经典写法。关键口诀：`n &= ~(0x1 << 17)` 清零第 17\n  位，`n |= 1 << 9` 把第 9 位置一。\n- **运算符优先级** — \"独眼龙比两只眼的正常人更狠\"（单目 > 双目），\"算比位逻\"（算术 > 比较 > 位 >\n  逻辑），三目倒数第三，赋值倒数第二，逗号最低。\n- **类型转换** — 自动提升（`char → int → long → float → double`）、强制转型语法\n  `(Type)expr`、宽转窄截断、浮点转整数直接去尾。\n\n### Rust\nRustlings iterators 部分全部做完，笔记见 [Rust/day6.md](30-rust-day5.md)。今天每道题都卡了很久，暴露出\n来的问题不是语法，而是**不知道标准库有哪些方法可用**：\n\n- `capitalize_first` — `chars()` + `as_str()` 分割首字母和余下部分\n- `(1..=num).product()` — 阶乘一行搞定，卡最久的一个\n- `flat_map` — 拍平嵌套迭代器（`&[HashMap<_,_>]` → 一条流）\n- `collect()` — 会根据返回值类型自动判断收集成 `Vec`、`String` 还是别的\n\n`map_err` 和 `ok()` + `?` 的配合用法也比昨天熟练了。\n\n## 思考\n**核心观点：人并非自主的主体，而是欲望驱动下的被动响应机器。**\n\n---\n### 一、什么是\"空壳\"\n\n\"空壳\"在这里指：人没有所谓\"自主的意志\"。你的每一个行动，背后都站着一种欲望。没有欲望作为燃料，人这台机器就不会运转。\n\n---\n\n### 二、两个论据\n\n#### 论据一：食欲——披着生存外衣的欲望\n\n饿的时候，你会为了一口吃的拼命。人们认为这理所当然，因为底层逻辑是\"为了生存\"。\n\n但换个角度看：你并不是在\"选择\"进食，你是在被饥饿感**驱使**着进食。你的行为只是一个生理信号的自动响应。\n\n#### 论据二：性欲——剥离生存借口后的纯粹欲望\n\n性欲与生存无关（个体不交配并不会死）。\n\n但当它出现时，人同样会倾尽所有去满足。这说明驱动力不是\"生存的必要性\"，而是**欲望的强度本身**。\n\n> 两个论据的共同结论：人的行为不是理性的自主选择，而是被欲望强度决定的。\n\n---\n\n### 三、为什么\"安逸\"是关键证据\n\n如果人是自主的，那在安逸的环境中，人应该照样行动。\n\n但事实相反：\n\n安逸 → 懒惰 → 停滞\n痛苦 → 欲望 → 行动\n\n人只有在**不舒服**的时候才会动。行动的目的永远指向**消除不舒服**、制造安逸。\n\n这说明：驱动源是负面的（痛苦/欲望），不是正面的（意志/理想）。人的行为是被\"推着走\"，不是\"朝着目标走\"。\n\n---\n\n### 四、核心模型：欲望循环\n```text\n欲望无法被满足\n    │\n    ▼\n痛苦（张力状态）\n    │\n    ▼\n改变（行动）\n    │\n    ▼\n满足（欲望被填补）\n    │\n    ▼\n欲望感知度降低（阈值升高）\n    │\n    ▼\n满足度下降\n    │\n    ▼\n痛苦（新的张力）\n    │\n    ▼\n改变 → 满足 → 阈值升高 → ...\n```\n> 这个循环没有终点。每一次满足都在抬高下一次满足的门槛。\n\n---\n\n### 五、类比：机器\n\n| | 机器 | 人 |\n|---|------|-----|\n| 能量耗尽 | 停止运转 | — |\n| 欲望消失 | — | 停止行动 |\n| 加燃料 | 重新运转 | — |\n| 新欲望产生 | — | 重新行动 |\n| 停止条件 | 零件损坏 | 死亡 |\n\n机器因为燃料耗尽而停，因为加油而动。\n\n人因为欲望满足而停，因为新欲望产生而动。\n\n唯一的停止方式：机器坏掉，或者人死掉。\n\n---\n\n### 六、结论\n\n人没有\"想不想被驱动\"的选择权——欲望产生的那一刻，驱动就已经发生了。\n\n所谓 **\"自由意志\"** ，可能只是我们在事后给自己编的理由。\n\n---\n\n\n## 明日计划\n- C 语言：继续跟课堂节奏\n- Rust：推进到 smart_pointers\n\n  ---",
            "url": "https://kyriechao.github.io/posts/2026/07/31-diary",
            "title": "运算符与迭代器收尾",
            "summary": "C 语言位运算符与类型转换，Rust iterators 收尾，挫败感与收获并存的一天",
            "date_modified": "2026-07-31T13:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/31-rust-day6",
            "content_html": "\n# Rust Day6 — Iterators 收尾\n\n> Rustlings iterators 部分全部做完。今天的题每一道都卡了很久，挫败感很强，但回头看也暴露了知识盲区——不知道某些函数的存在，就不可能想出对应的解法。\n\n---\n\n## 一、首字母大写 — `chars()` 迭代器\n\n```rust\n// \"hello\" -> \"Hello\"\nfn capitalize_first(input: &str) -> String {\n    let mut ch = input.chars();\n    match ch.next() {\n        Some(c) => c.to_uppercase().to_string() + ch.as_str(),\n        None => String::from(\"\"),\n    }\n}\n```\n\n**拆解：**\n\n1. `input.chars()` — 返回字符串的**字符迭代器**，不是字节迭代器\n2. `ch.next()` — 消费第一个字符，剩余字符还在 `ch` 里\n3. `c.to_uppercase()` — 返回一个 `Uppercase` 迭代器（因为有些字符大写后不止一个字符，如 ß → SS），用 `to_string()` 收成 `String`\n4. `ch.as_str()` — 把迭代器剩余部分直接当 `&str` 取出来\n5. 拼接 → 首字母大写完成\n\n### 对切片批量应用 — `map` + `collect`\n\n```rust\n// [\"hello\", \"world\"] -> [\"Hello\", \"World\"]\nfn capitalize_words_vector(words: &[&str]) -> Vec<String> {\n    words\n        .iter()\n        .map(|n| {\n            let mut s = n.chars();\n            match s.next() {\n                Some(k) => k.to_uppercase().to_string() + s.as_str(),\n                None => String::from(\"\"),\n            }\n        })\n        .collect()\n}\n\n// [\"hello\", \" \", \"world\"] -> \"Hello World\"\nfn capitalize_words_string(words: &[&str]) -> String {\n    words\n        .iter()\n        .map(|s| {\n            let mut chars = s.chars();\n            match chars.next() {\n                None => String::new(),\n                Some(f) => f.to_uppercase().to_string() + chars.as_str(),\n            }\n        })\n        .collect()\n}\n```\n\n> **关键收获：`collect()` 会根据返回类型自动判断收集成什么。** 同样一串 `map` 链，返回值写 `Vec<String>` 就收集成 Vec，写 `String` 就收集成拼接字符串——因为 `String` 也实现了 `FromIterator`。\n\n### `collect()` 类型推断机制\n\n```rust\n// 同一个迭代器链，collect 出不同结果：\nlet v: Vec<String> = words.iter().map(...).collect();  // 返回 Vec\nlet s: String      = words.iter().map(...).collect();  // 返回 String（拼接）\n```\n\n| 目标类型            | collect 行为               |\n| ------------------- | -------------------------- |\n| `Vec<String>`       | 每个 `String` push 进 Vec  |\n| `String`            | 把所有 `String` 拼接成一个 |\n| `HashMap<_, _>`     | 收集键值对                 |\n| `Result<Vec<_>, _>` | 遇到第一个 Err 就停        |\n\n> 编译器通过函数签名里的返回类型倒推 `collect` 该生成什么——这就是 Rust 的类型推断从返回值\"反向传播\"的能力。\n\n---\n\n## 二、`divide` — 自定义错误枚举\n\n```rust\nenum DivisionError {\n    DivideByZero,       // 42 / 0\n    IntegerOverflow,    // i64::MIN / -1 → 结果超出 i64::MAX\n    NotDivisible,       // 5 / 2 = 2.5 不能整除\n}\n\nfn divide(a: i64, b: i64) -> Result<i64, DivisionError> {\n    if b == 0 {\n        Err(DivisionError::DivideByZero)\n    } else if a == i64::MIN && b == -1 {\n        Err(DivisionError::IntegerOverflow)\n    } else if a % b == 0 {\n        Ok(a / b)\n    } else {\n        Err(DivisionError::NotDivisible)\n    }\n}\n```\n\n> `i64::MIN / -1` 会溢出，因为 `i64::MIN = -9223372036854775808`，取绝对值后比 `i64::MAX` 大 1，这是有符号整数的经典陷阱。\n\n### 2.1 `ok()` + `?` — Result 转 Option 并短路\n\n```rust\n// 期望输出: Ok([1, 11, 1426, 3])\nfn result_with_list() -> Option<Vec<i32>> {\n    let numbers = [27, 297, 38502, 81];\n    let mut vec = Vec::new();\n    for &n in &numbers {\n        let v = divide(n, 27).ok()? as i32;  // Result → Option，Err 时 ? 短路\n        vec.push(v);\n    }\n    Some(vec)\n}\n```\n\n**`ok()` 做了什么：**\n\n```rust\nResult::Ok(v)  --ok()-->  Some(v)\nResult::Err(e) --ok()-->  None       // 错误信息直接丢弃\n```\n\n> 然后 `?` 在 `Option` 上下文中：遇到 `None` 直接 return `None`。一行代码把两件事都干了。\n\n### 2.2 `map` + `map_err` — 保留错误但不中断\n\n```rust\n// 期望输出: [Ok(1), Ok(11), Ok(1426), Ok(3)]\nfn list_of_results() -> Vec<Result<i32, &'static str>> {\n    let numbers = [27, 297, 38502, 81];\n    numbers\n        .into_iter()\n        .map(|n| {\n            divide(n, 27)\n                .map(|v| v as i32)           // Ok 分支：类型转换\n                .map_err(|_| \"division error\") // Err 分支：换一个错误信息\n        })\n        .collect()\n}\n```\n\n| 方法      | 作用                             |\n| --------- | -------------------------------- |\n| `map`     | 只碰 `Ok` 里的值，`Err` 原样透传 |\n| `map_err` | 只碰 `Err` 里的值，`Ok` 原样透传 |\n| `ok()`    | `Result` → `Option`，丢弃错误    |\n\n---\n\n## 三、`(1..=num).product()` — 阶乘一行版\n\n```rust\nfn factorial(num: u64) -> u64 {\n    (1..=num).product()\n}\n```\n\n> 这道题卡了最久。知道阶乘就是 `1 * 2 * 3 * … * n`，脑子里想的都是递归或 `fold`，完全不知道标准库自带 `product()`。一行解决后整个人愣住了。\n\n`product()` 对迭代器做连乘——类似 `sum()` 做连加。只用范围和方法调用，没有显式循环、没有中间变量、没有递归。\n\n---\n\n## 四、`filter` + `count` — 统计满足条件的元素\n\n```rust\n#[derive(PartialEq)]  // 需要 PartialEq 才能用 == 比较\nenum Progress {\n    None,\n    Some,\n    Complete,\n}\n\nfn count_iterator(map: &HashMap<String, Progress>, value: Progress) -> usize {\n    map.values().filter(|&v| *v == value).count()\n}\n```\n\n原来 for 循环版 5 行，迭代器版 1 行：`filter` 筛出匹配的，`count` 计数。\n\n---\n\n## 五、`flat_map` — 拍平嵌套结构\n\n### 传统写法（双层 for）\n\n```rust\nfn count_collection_for(collection: &[HashMap<String, Progress>], value: Progress) -> usize {\n    let mut count = 0;\n    for map in collection {\n        for val in map.values() {\n            if *val == value {\n                count += 1;\n            }\n        }\n    }\n    count\n}\n```\n\n### 迭代器版 — `flat_map` 把内层\"拍平\"\n\n```rust\nfn count_collection_iterator(collection: &[HashMap<String, Progress>], value: Progress) -> usize {\n    collection\n        .iter()                              // 遍历外层 slice\n        .flat_map(|map| map.values())        // 把每个 map 的 values 拍平成一个流\n        .filter(|&v| *v == value)            // 筛选\n        .count()                             // 计数\n}\n```\n\n**`flat_map` 做了什么：**\n\n```\n输入: [{map1}, {map2}, {map3}]\n         ↓ flat_map(|m| m.values())\n输出: [v1, v2, v3, v4, v5, ...]   ← 所有 map 的 values 连成一条流\n```\n\n> `flat_map` 卡住是因为不知道有这个函数。知道之后就简单了：先 `map` 拿到多个 values 迭代器，再 `flat` 把它们拍平。本质上等价于双层 for 循环。\n\n---\n\n## 六、今天踩的坑 & 反思\n\n今天每道题都卡了很久。核心原因不是 Rust 语法难，而是**不知道标准库提供了什么**：\n\n| 卡住的地方         | 原因                                  | 解决方法                  |\n| ------------------ | ------------------------------------- | ------------------------- |\n| `capitalize_first` | 不知道 `chars()` + `as_str()` 组合    | 查 API                    |\n| `factorial`        | 完全不知道 `product()` 存在           | AI 告诉的                 |\n| `result_with_list` | 知道 `ok()` 但没想到 `?` 可以接在后面 | 多练                      |\n| `list_of_results`  | 知道 `map_err` 但犹豫怎么拼起来       | 多练                      |\n| `flat_map`         | 不知道这个函数                        | AI 告诉的                 |\n| `collect` 返回不同 | 一直以为只能收集成 Vec                | 今天理解了 `FromIterator` |\n\n**这种挫败感是正常的。** 迭代器是 Rust 里最\"需要积累\"的部分——标准库给迭代器实现了上百个方法（`product`、`flat_map`、`filter_map`、`take_while`、`partition`……），多数人都是边查边写，见一个记一个。今天的代码写完，至少这几个方法你不会忘了。\n\n---\n\n## 七、剩余进度\n\n```\n✅ iterators          §13.2-4    — 今天完成\n⬜ smart_pointers     §15, §16.3\n⬜ threads            §16.1-3\n⬜ macros             §20.5\n⬜ clippy             Appendix D\n⬜ conversions        n/a\n```\n\n**明天目标：** lifetimes。",
            "url": "https://kyriechao.github.io/posts/2026/07/31-rust-day6",
            "title": "Rust 学习计划",
            "summary": "Rust 第6天学习",
            "date_modified": "2026-07-31T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/30-diary",
            "content_html": "\n## 技术\n\n### Rust\nRustlings 测试全部做完了。今天集中写了闭包、迭代器、智能指针的练习代码，笔记见\n[Rust/day5.md](30-rust-day5.md)。\n\n新接触的内容：\n- 闭包的三种 trait：`Fn`、`FnMut`、`FnOnce`\n- `move` 关键字不等于\"只能调用一次\"\n- `Box<T>`、`Rc<T>`、`RefCell<T>` 各自解决了什么问题\n- `Rc<RefCell<T>>` 组合实现单线程下的共享可变状态\n\n### C 语言\n今天讲运算符，涉及按位运算：\n\n| 运算符 | 含义 | 示例 |\n  |---|---|---|\n| `\\|` | 按位或 | `0b0011 \\| 0b0101 = 0b0111` |\n| `&` | 按位与 | `0b0011 & 0b0101 = 0b0001` |\n| `~` | 按位取反 | `~0b0011 = ...11111100`（补码） |\n| `^` | 按位异或 | `0b0011 ^ 0b0101 = 0b0110` |\n\n## 明日计划\n- C 语言：继续跟课堂，运算符这块偏底层，值得细看\n- Rust：推进到 lifetimes",
            "url": "https://kyriechao.github.io/posts/2026/07/30-diary",
            "title": "2026-07-30 周四 · Rustlings 收尾",
            "summary": "Rustlings 测试全部完成，C 语言进入运算符，Rust 推进到智能指针",
            "date_modified": "2026-07-30T13:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/30-rust-day5",
            "content_html": "\n# Rust Day5 — 闭包、迭代器与智能指针\n\n> Rustlings 测试全部完成，今天集中写代码练习。\n\n---\n\n## 一、Option 补充方法\n\n### 1.1 `filter` — 按条件过滤 Some 值\n\n```rust\nfn main() {\n    let x = Some(5);\n    let even = x.filter(|n| n % 2 == 0);\n    println!(\"{even:?}\");  // None — 5 不是偶数\n\n    let y = Some(4);\n    let even = y.filter(|n| n % 2 == 0);\n    println!(\"{even:?}\");  // Some(4)\n}\n```\n\n> `filter` 接收一个谓词闭包：若为 `Some` 且条件满足 → 保持不变；否则 → `None`。不会改变内部值，只决定\"通过\"还是\"拦截\"。\n\n### 1.2 `transpose` — Option 与 Result 相互翻转\n\n```rust\nlet x: Option<Result<i32, String>> = Some(Err(\"bad\".to_string()));\nlet y = x.transpose();\nprintln!(\"{y:?}\");  // Err(\"bad\")\n```\n\n| 输入           | `transpose()` 输出 |\n| -------------- | ------------------ |\n| `Some(Ok(v))`  | `Ok(Some(v))`      |\n| `Some(Err(e))` | `Err(e)`           |\n| `None`         | `Ok(None)`         |\n\n> 常见场景：把 `Option<Result<T, E>>` 翻成 `Result<Option<T>, E>`，方便用 `?` 传播错误。\n\n---\n\n## 二、闭包（Closures）\n\n### 2.1 基础闭包与类型推断\n\n```rust\n// 闭包自动推断参数和返回类型\nlet add = |a, b| a + b;\nlet x = add(1, 2);\nprintln!(\"{x}\");  // 3\n```\n\n> 第一次调用 `add(1, 2)` 时编译器推断出 `a: i32, b: i32 -> i32`。之后类型就锁定了，不能再传 `&str` 之类。\n\n### 2.2 捕获外部变量\n\n```rust\nlet factor = 2;\nlet multiply = |n| n * factor;  // 不可变借用 factor\n\nprintln!(\"{}\", multiply(5));   // 10\nprintln!(\"{}\", multiply(10));  // 20 —— 闭包可以多次调用\n```\n\n> 这个闭包只借用了 `factor`（不可变引用），实现了 `Fn` trait，可以反复调用。\n\n### 2.3 `FnMut` — 可变借用捕获\n\n```rust\nlet mut count = 0;\n\nlet mut inc = || {\n    count += 1;   // 可变借用 count\n    count\n};\n\nprintln!(\"{}\", inc());  // 1\nprintln!(\"{}\", inc());  // 2\nprintln!(\"{}\", inc());  // 3\n```\n\n> 闭包持有 `&mut count`，每调用一次 +1。这类闭包实现的是 `FnMut` trait。\n\n### 2.4 `move` — 强制所有权转移\n\n```rust\nlet s = String::from(\"hello\");\n\nlet f = move || {\n    println!(\"{}\", s);  // s 的所有权移入闭包\n};\nf();\n// println!(\"{}\", s);  // ❌ s 已被移走，编译不通过\n```\n\n> **纠正一个常见误区：** `move` 关键字表示\"按值捕获\"（把所有权拿进来），但闭包**仍然可以多次调用**。只有实现了 `FnOnce` 的闭包才只能调用一次。这里 `println!` 只是借用了 `s`，所以 `f` 实现的是 `Fn`，可以反复调用。\n\n| trait    | 调用方式                | 典型场景                  |\n| -------- | ----------------------- | ------------------------- |\n| `Fn`     | `&self`，可多次调用     | 只读捕获                  |\n| `FnMut`  | `&mut self`，可多次调用 | 修改捕获的变量            |\n| `FnOnce` | `self`，只能调一次      | 消费捕获的值（如 `drop`） |\n\n---\n\n## 三、迭代器（Iterators）\n\n### 3.1 手动迭代 — `next()`\n\n```rust\nlet v = vec![1, 2, 3];\nlet mut iter = v.iter();\n\nprintln!(\"{:?}\", iter.next());  // Some(1)\nprintln!(\"{:?}\", iter.next());  // Some(2)\nprintln!(\"{:?}\", iter.next());  // Some(3)\nprintln!(\"{:?}\", iter.next());  // None —— 迭代结束\n```\n\n### 3.2 常用消费者（Consumers）\n\n```rust\nlet sum: i32 = v.iter().sum();          // 求和\nlet count = v.iter().count();           // 计数\nlet collected: Vec<i32> = v.iter().copied().collect();  // 收集到 Vec\n```\n\n> `copied()` 把 `&i32` 转成 `i32`（对 `Copy` 类型适用），然后 `collect()` 收进 `Vec<i32>`。\n\n### 3.3 `filter` + `map` — 经典组合\n\n```rust\nlet v2 = vec![1, 2, 3, 4, 5];\n\nlet result: Vec<i32> = v2\n    .iter()\n    .filter(|&&x| x % 2 == 0)  // 保留偶数\n    .map(|&x| x * 2)            // 翻倍\n    .collect();\n\nprintln!(\"{result:?}\");  // [4, 8]\n```\n\n> **注意双重引用 `&&x`：** `v2.iter()` 产生 `&i32`，`filter` 再借一次变成 `&&i32`，所以需要 `&&x` 来解两层引用，或者直接用 `|x| **x % 2 == 0`。\n\n### 3.4 `fold` — 累积归约\n\n```rust\nlet sum = v2.iter().fold(0, |acc, &x| acc + x);\nprintln!(\"{}\", sum);  // 15\n```\n\n| 方法            | 输入                       | 输出         |\n| --------------- | -------------------------- | ------------ |\n| `fold(init, f)` | 迭代器 + 初始值 + 累积函数 | 单值         |\n| 等价于          | 循环 `acc = f(acc, item)`  | 最终累积结果 |\n\n### 3.5 `zip` — 并行迭代两个集合\n\n```rust\nlet names = vec![\"Alice\", \"Bob\"];\nlet scores = vec![85, 90];\n\nfor (name, score) in names.iter().zip(scores.iter()) {\n    println!(\"{} {}\", name, score);\n}\n// Alice 85\n// Bob 90\n```\n\n> 两个迭代器按位置配对，较短的耗尽时停止。\n\n---\n\n## 四、智能指针\n\n### 4.1 `Box<T>` — 堆分配\n\n```rust\nuse std::{cell::RefCell, rc::Rc};\n\nenum List {\n    Cons(i32, Box<List>),\n    Nil,\n}\n\nfn main() {\n    let b = Box::new(5);\n    println!(\"{b}\");  // 5\n\n    // let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));\n    // println!(\"{list:?}\");\n}\n```\n\n> Rust 要求编译期知道每个类型的大小。`List` 是递归的，编译器算不出大小，所以用 `Box<List>` 把递归部分放堆上——`Box` 是指针，大小固定。\n\n### 4.2 `Rc<T>` — 引用计数（单线程共享所有权）\n\n```rust\nlet data = Rc::new(String::from(\"hello\"));\nlet data2 = Rc::clone(&data);\n\nprintln!(\"引用计数: {}\", Rc::strong_count(&data));  // 2\nprintln!(\"{}\", data2);  // hello\n```\n\n> `Rc::clone` 不深拷贝数据，只增加引用计数。`Rc<T>` **不是线程安全**的（没有 `Send`/`Sync`），多线程场景用 `Arc<T>`。\n\n### 4.3 `RefCell<T>` — 内部可变性（运行时借用检查）\n\n```rust\nlet data = RefCell::new(5);\n{\n    let mut ref_mut = data.borrow_mut();\n    *ref_mut += 1;\n}  // ref_mut 在此 drop，释放可变借用\nprintln!(\"{}\", data.borrow());  // 6\n```\n\n| 对比     | `&mut T`（编译期） | `RefCell<T>`（运行时） |\n| -------- | ------------------ | ---------------------- |\n| 检查时机 | 编译期             | 运行时                 |\n| 违规后果 | 编译错误           | **panic**              |\n| 灵活性   | 受限于借用规则     | 可在不可变上下文中修改 |\n\n> **关键规则：** 同一作用域内只能有一个 `borrow_mut()`，或者多个 `borrow()`，不能同时——这和 `&mut` / `&` 的规则一样，只不过从编译期挪到了运行时。\n\n### 4.4 `Rc<RefCell<T>>` — 组合拳：共享 + 可变\n\n```rust\nlet data = Rc::new(RefCell::new(5));\nlet data2 = Rc::clone(&data);\n\n*data2.borrow_mut() += 1;    // 通过 data2 修改内部值\n\nprintln!(\"{}\", data.borrow());   // 6 —— data 也看到了变化\nprintln!(\"{:?}\", data2);         // RefCell { value: 6 }\n```\n\n> `Rc` 允许多个所有者，`RefCell` 允许在不可变引用下修改内部值。组合起来就是**多个所有者都可以修改同一块数据**——类似于单线程下的共享可变状态。\n\n---\n\n## 五、本日小结\n\n| 知识点                             | 掌握程度 | 说明                             |\n| ---------------------------------- | -------- | -------------------------------- |\n| `Option::filter`                   | 会用     | 条件过滤 Some，不满足转 None     |\n| `Option::transpose`                | 会用     | Option<Result> ↔ Result<Option>  |\n| 闭包 `\\|a\\| a + b`                 | 会用     | 自动推断类型，捕获环境变量       |\n| `Fn` / `FnMut` / `FnOnce`          | 了解     | 三种闭包 trait，决定调用方式     |\n| `move` 闭包                        | 会用     | 强制按值捕获，不等于\"只能调一次\" |\n| 迭代器 `filter`/`map`/`fold`/`zip` | 会用     | 链式组合，惰性求值               |\n| `Box<T>`                           | 会用     | 堆分配，递归类型必用             |\n| `Rc<T>`                            | 会用     | 单线程引用计数，共享所有权       |\n| `RefCell<T>`                       | 会用     | 运行时借用检查，内部可变性       |\n| `Rc<RefCell<T>>`                   | 会用     | 组合：多个所有者 + 可变          |\n\n**明天目标：** 推进到 lifetimes（生命周期标注）。",
            "url": "https://kyriechao.github.io/posts/2026/07/30-rust-day5",
            "title": "Rust 学习计划",
            "summary": "Rust 第5天学习",
            "date_modified": "2026-07-30T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/29-diary",
            "content_html": "\n## 技术\n\n### Rust\n今天只推进了 Options 的内容，笔记见 [Rust/day4.md](29-rust-day4.md)。进度不快，但 Options 这部分涉及 `and_then`、`ok_or`、`?` 运算符的组合用法，值得花时间吃透。\n\n### C 语言\n课堂内容开始步入正轨，不再是纯铺垫了。\n\n## 生活\n域名备案今天通过了，算是本周一个小里程碑。\n\n## 明日计划\n- Rust 保持节奏慢慢推进，不赶进度\n- 重心放在 C 语言上，优先跟紧课堂",
            "url": "https://kyriechao.github.io/posts/2026/07/29-diary",
            "title": "日志",
            "summary": "域名备案通过，Rust Options 收尾，C 语言步入正轨",
            "date_modified": "2026-07-29T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/29-rust-day4",
            "content_html": "\n\n\n# Rust Day4 — Option 与 Result\n\n## 一、Rustlings 练习\n\n```rust\n// 根据当前时间返回冰箱里剩余的冰淇淋勺数\n// 22:00 之前 -> 5 勺，22:00 之后 -> 0 勺（被吃光了）\n// 如果 hour_of_day > 23（非法时间），返回 None\nfn maybe_ice_cream(hour_of_day: u16) -> Option<u16> {\n    match hour_of_day {\n        hour if hour < 22 => Some(5),\n        hour if hour >= 22 && hour < 24 => Some(0),\n        _ => None,\n    }\n}\n\nfn main() {\n    // You can optionally experiment here.\n}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn raw_value() {\n        // 用 unwrap() 取出 Option 里包裹的值\n        let ice_creams = maybe_ice_cream(12).unwrap();\n\n        assert_eq!(ice_creams, 5); // Don't change this line.\n    }\n\n    #[test]\n    fn check_ice_cream() {\n        assert_eq!(maybe_ice_cream(0), Some(5));\n        assert_eq!(maybe_ice_cream(9), Some(5));\n        assert_eq!(maybe_ice_cream(18), Some(5));\n        assert_eq!(maybe_ice_cream(22), Some(0));\n        assert_eq!(maybe_ice_cream(23), Some(0));\n        assert_eq!(maybe_ice_cream(24), None);\n        assert_eq!(maybe_ice_cream(25), None);\n    }\n}\n```\n\n> 今天就做了一道 Rustlings 题。Options 概念学完了，但练习题还没做完。\n\n---\n\n## 二、自定义练习：模拟用户查询\n\n### 2.1 User 结构体与查找\n\n```rust\nstruct User {\n    id: u32,\n    name: String,\n    age: Option<u32>,      // 年龄可能未填写\n    email: Option<String>, // 邮箱可能未填写\n}\n\nfn find_user_by_id(id: u32) -> Option<User> {\n    // 模拟数据库查询——查不到返回 None\n    if id == 1 {\n        Some(User {\n            id: 1,\n            name: \"Alice\".to_string(),\n            age: Some(25),\n            email: Some(\"alice@example.com\".to_string()),\n        })\n    } else if id == 2 {\n        Some(User {\n            id: 2,\n            name: \"Bob\".to_string(),\n            age: None,\n            email: None,\n        })\n    } else {\n        None\n    }\n}\n```\n\n> 这里 `age` 和 `email` 都用 `Option<T>` 表示\"可有可无\"的字段——这正是 Rust 没有 `null` 的替代方案。\n\n### 2.2 从邮箱提取域名 — `and_then` + `split`\n\n```rust\nfn get_user_email_domain(user: &User) -> Option<String> {\n    // 从 email 里提取 @ 后面的部分\n    // 例如 \"alice@example.com\" -> \"example.com\"\n    // 如果 email 是 None，返回 None\n    user.email\n        .as_ref()                                       // Option<&String>（借用，不转移所有权）\n        .and_then(|email| email.split(\"@\").nth(1).map(|o| o.to_string()))\n}\n```\n\n**链式调用拆解：**\n\n1. `user.email.as_ref()` → 得到 `Option<&String>`，避免把 `email` 移出 `user`\n2. `and_then(...)` → 如果 `None`，短路返回 `None`；否则用闭包处理内部值\n3. `split(\"@\").nth(1)` → 按 `@` 分割，取第 2 段（索引 1），返回 `Option<&str>`\n4. `.map(|o| o.to_string())` → 将 `&str` 转为 `String`\n\n### 2.3 年龄段判断 — `match` + 守卫\n\n```rust\nfn get_user_age_group(user: &User) -> String {\n    match user.age {\n        Some(age) if age < 18           => String::from(\"未成年\"),\n        Some(age) if age >= 18 && age < 60 => String::from(\"成年人\"),\n        Some(_)                         => String::from(\"老年人\"),  // age >= 60\n        None                            => String::from(\"未知\"),\n    }\n}\n```\n\n> **改进点：** 去掉了原版冗余的 `Some(_) | None` 分支——三条 `Some` 守卫已经覆盖了所有年龄值，`None` 单独兜底即可。\n\n### 2.4 调用层 — `if let` 解包\n\n```rust\nfn main() {\n    let user1 = find_user_by_id(1);\n    let user2 = find_user_by_id(2);\n    let user3 = find_user_by_id(99);\n\n    // 打印 user1 的邮箱域名\n    if let Some(u) = &user1 {\n        println!(\"邮箱域名: {:?}\", get_user_email_domain(u));\n    }\n\n    // 打印各用户的年龄段\n    if let Some(u) = &user1 {\n        println!(\"user1 年龄段: {}\", get_user_age_group(u));\n    }\n    if let Some(u) = &user2 {\n        println!(\"user2 年龄段: {}\", get_user_age_group(u));\n    }\n    if let Some(u) = &user3 {\n        println!(\"user3 年龄段: {}\", get_user_age_group(u));\n    } else {\n        println!(\"user3 未找到\");\n    }\n}\n```\n\n> `if let Some(u) = &user` 的 `&` 是借用的关键——`find_user_by_id` 返回 `Option<User>`，所有权还在 `user1` 身上，用 `&` 借用来匹配就不会把值移走。\n\n---\n\n## 三、Option 转 Result — 配置读取\n\n### 3.1 核心函数\n\n```rust\nfn get_config_value(key: &str) -> Option<String> {\n    // 模拟从配置里读取键值对\n    let config = vec![(\"name\", \"Alice\"), (\"age\", \"25\"), (\"email\", \"\")];\n\n    config\n        .iter()\n        .find(|(k, _)| *k == key)   // 找到第一个匹配的元组，返回 Option<&&(&str, &str)>\n        .map(|(_, v)| v.to_string())  // 取出值并转为 String\n}\n\nfn parse_age(s: &str) -> Result<u32, String> {\n    s.parse().map_err(|_| format!(\"'{}' 不是有效年龄\", s))\n}\n```\n\n### 3.2 `get_user_age` — `?` + 组合子版\n\n```rust\nfn get_user_age() -> Result<u32, String> {\n    // 1. 获取 age 配置，若为 None -> Err\n    // 2. 若为空字符串 -> Err\n    // 3. 否则调用 parse_age 解析\n    //\n    // 要求：尽量用 ? 和组合子，减少 match 嵌套\n\n    let age_str = get_config_value(\"age\").ok_or(\"缺少 age 配置\")?;\n\n    if age_str.is_empty() {\n        return Err(String::from(\"age 不能为空\"));\n    }\n\n    parse_age(&age_str)\n}\n```\n\n**改进说明（对比原版）：**\n\n| 原版 `if let`      | 改进版 `ok_or` + `?`                            |\n| ------------------ | ----------------------------------------------- |\n| 4 行 + 嵌套 else   | 1 行，链式语义                                  |\n| 手动处理 None 分支 | `ok_or` 把 `Option` 转成 `Result`，`?` 自动短路 |\n\n> `ok_or` 的用法：`Option → Result`，`None` 时变成你指定的 `Err`。配上 `?`，None 时直接 return Err，Some 时自动 unwrap。\n\n### 3.3 调用\n\n```rust\nfn main() {\n    match get_user_age() {\n        Ok(age) => println!(\"用户年龄: {}\", age),\n        Err(e)  => println!(\"错误: {}\", e),\n    }\n}\n```\n\n---\n\n## 四、`?` 运算符链式调用 — 数据库查询模拟\n\n```rust\nstruct Database;\nstruct Connection;\n\nfn connect_db() -> Option<Database>     { Some(Database) }\nfn get_conn(db: &Database) -> Option<Connection> { Some(Connection) }\nfn query_user(conn: &Connection, id: u32) -> Option<String> { Some(\"Alice\".to_string()) }\n\nfn get_username(user_id: u32) -> Option<String> {\n    let db = connect_db()?;\n    let conn = get_conn(&db)?;\n    let user = query_user(&conn, user_id)?;\n    Some(user.to_uppercase())\n}\n```\n\n> 注释掉的那一大段 `if db.is_none()` + `unwrap()` 就是这四个 `?` 的**手动展开版**。`?` 的本质：遇到 `None` 就提前 return，本质是语法糖，但相比手动判断可读性提升巨大。\n\n---\n\n## 五、本日小结\n\n| 知识点                 | 掌握程度 | 说明                               |\n| ---------------------- | -------- | ---------------------------------- |\n| `Option<T>` 概念       | 已学完   | 替代 null，表达\"值可有可无\"        |\n| `unwrap()`             | 会用     | 测试或已知 Some 时取值，panic 风险 |\n| `if let Some(x) = ...` | 会用     | 只关心 Some 分支时的简洁写法       |\n| `match` + 守卫         | 会用     | 配合 `if` 条件做精细匹配           |\n| `.as_ref()`            | 会用     | 借用 Option 内部值，不转移所有权   |\n| `.and_then()`          | 会用     | Option 链式处理，None 自动短路     |\n| `ok_or()` + `?`        | 会用     | Option → Result 转换               |\n| `?` 运算符             | 会用     | 链式调用，自动传播 None/Err        |\n| `Result<T, E>`         | 初识     | 配合 Option 做错误处理             |\n\n**明天目标：** Options 剩余练习题收尾，推进到 lifetimes。",
            "url": "https://kyriechao.github.io/posts/2026/07/29-rust-day4",
            "title": "Rust 学习计划",
            "summary": "Rust 第4天学习",
            "date_modified": "2026-07-29T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/28-diary",
            "content_html": "# 2026-07-28 19:42:07 +0800\n\n## 技术\n\n### Rust\n继续推进 Rust 学习，今天发现了一个练手工具 **Rustlings**，可以跟着它的练习题逐步巩固语法。\n\n当前进度：变量、函数、条件判断、基本类型、Vec、移动语义、结构体、枚举、字符串、模块、HashMap 已完成，**正在做 options 相关练习**。\n\n明天目标是推进到 **lifetimes**，这部分不能求快，得稳扎稳打。\n\n### C 语言\n机构老师今天开始讲 C 语言了，从变量切入。虽然 C 我很熟，但今天还是碰到了之前没深究过的知识点：\n\n- 基础类型的表数范围\n- 浮点类型在内存中的存储形态（IEEE 754 标准）\n\n这两个属于\"以为懂了，其实没细想过\"的内容，正好借机补上。\n\n## 生活\n越是基础的东西越容易眼高手低。C 语言这块，我不打算因为自己有底子就轻视它——沉下心重新过一遍，说不定能发现更多\"以为懂了\"的盲区。\n\n## 明日计划\n- C 语言：跟课堂节奏，重点消化基础类型和存储相关的细节\n- Rust：完成 options 练习，推进到 lifetimes\n- 权重上 C > Rust，优先保证课堂内容吃透\n",
            "url": "https://kyriechao.github.io/posts/2026/07/28-diary",
            "title": "日志",
            "summary": "Rust 继续推进 options 练习，C 语言从变量开始，温故知新",
            "date_modified": "2026-07-28T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/28-rust-day3",
            "content_html": "# Rust 基础语法（Day 3）— Rustlings 实战练习\n\n今天接触了 **Rustlings**——Rust 官方出品的交互式练习工具，通过修复小练习来掌握 Rust 语法，比干读书有意思多了。\n\n---\n\n## 1. 练习一：变量、函数与条件判断\n\n> 对应章节：Variables（§3.1）、Functions（§3.3）、If（§3.5）\n\n**题目：** Mary 买苹果，每个苹果 2 rustbucks。如果购买数量超过 40 个，则每个苹果降至 1 rustbuck。编写函数计算总价。\n\n```rust\nfn calculate_price_of_apples(n: i32) -> i32 {\n    if n > 40 {\n        n\n    } else {\n        n * 2\n    }\n}\n\nfn main() {}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[test]\n    fn verify_test() {\n        assert_eq!(calculate_price_of_apples(35), 70);\n        assert_eq!(calculate_price_of_apples(40), 80);\n        assert_eq!(calculate_price_of_apples(41), 41);\n        assert_eq!(calculate_price_of_apples(65), 65);\n    }\n}\n```\n\n**关键知识点：**\n\n| 知识点                        | 说明                                                   |\n| ----------------------------- | ------------------------------------------------------ |\n| `fn 函数名(参数) -> 返回类型` | Rust 函数定义语法，`->` 后跟返回值类型                 |\n| `if/else` 是**表达式**        | 可以直接返回值，不需要 `return` 关键字（当然也可以用） |\n| 表达式末尾不加分号            | `n` 和 `n * 2` 后面没有 `;`，表示这是返回值            |\n| `i32`                         | 32 位有符号整数，Rust 默认整数类型                     |\n| `#[test]`                     | 属性宏，标记测试函数                                   |\n| `assert_eq!`                  | 断言宏，左右两值相等则测试通过                         |\n\n---\n\n## 2. 练习二：字符串、Vec、枚举与模式匹配\n\n> 对应章节：Strings（§8.2）、Vecs（§8.1）、Move Semantics（§4.1-2）、Modules（§7）、Enums（§6）\n\n**题目：** 实现 `transformer` 函数，输入字符串和命令的元组列表，根据命令对字符串做三种操作之一：转大写、去首尾空格、追加 N 个 \"bar\"。\n\n```rust\nenum Command {\n    Uppercase,\n    Trim,\n    Append(usize),      // 枚举成员可以携带数据！\n}\n\nmod my_module {\n    use super::Command;\n\n    pub fn transformer(input: Vec<(String, Command)>) -> Vec<String> {\n        let mut results = Vec::new();\n        for eve in input {\n            let trans = match eve.1 {\n                Command::Uppercase => eve.0.to_uppercase(),\n                Command::Trim      => eve.0.trim().to_string(),\n                Command::Append(n) => {\n                    let mut s = eve.0.clone();\n                    s.push_str(&\"bar\".repeat(n));\n                    s\n                }\n            };\n            results.push(trans);\n        }\n        results\n    }\n}\n\nfn main() {}\n\n#[cfg(test)]\nmod tests {\n    use super::Command;\n    use crate::my_module::transformer;    // 从模块中导入函数\n\n    #[test]\n    fn it_works() {\n        let input = vec![\n            (\"hello\".to_string(), Command::Uppercase),\n            (\" all roads lead to rome! \".to_string(), Command::Trim),\n            (\"foo\".to_string(), Command::Append(1)),\n            (\"bar\".to_string(), Command::Append(5)),\n        ];\n        let output = transformer(input);\n\n        assert_eq!(\n            output,\n            [\n                \"HELLO\",\n                \"all roads lead to rome!\",\n                \"foobar\",\n                \"barbarbarbarbarbar\",\n            ]\n        );\n    }\n}\n```\n\n**关键知识点：**\n\n| 知识点                   | 说明                                                         |\n| ------------------------ | ------------------------------------------------------------ |\n| `enum` 成员可携带数据    | `Append(usize)` 表示该成员包含一个 `usize` 类型的值          |\n| `match` 模式匹配         | Rust 的 match 是**穷尽**的，所有枚举成员都必须处理           |\n| `String` vs `&str`       | `String` 是拥有所有权的堆分配字符串，`&str` 是借用的字符串切片 |\n| `to_uppercase()`         | 返回新的 `String`，原字符串不变                              |\n| `trim()`                 | 返回 `&str` 切片（不拷贝数据），需要 `to_string()` 转为 `String` |\n| `\"bar\".repeat(n)`        | 将 \"bar\" 重复 n 次，返回 `String`                            |\n| `mod` / `pub` / `use`    | Rust 模块系统三件套——定义模块、公开函数、导入路径            |\n| `clone()`                | 显式克隆数据，获得独立所有权（String 不实现 Copy）           |\n| 元组 `(String, Command)` | 用 `.0`、`.1` 按索引访问元组元素                             |\n\n> Rust 核心概念——**所有权（Ownership）**：`eve.0` 是 `String` 类型，直接使用会转移所有权。因此 `Append` 分支中用 `eve.0.clone()` 克隆了一份数据，避免所有权转移后原变量失效。\n\n---\n\n## 3. 练习三：HashMap 与结构体\n\n> 对应章节：HashMaps（§8.3）、Structs（§5.1）\n\n**题目：** 给定足球比赛结果（每行 `\"队伍1,队伍2,进球1,进球2\"`），构建计分表，记录每支球队的总进球数和总失球数。\n\n```rust\nuse std::collections::HashMap;\n\n#[derive(Default)]               // 自动实现 Default trait，方便构造默认值\nstruct TeamScores {\n    goals_scored: u8,\n    goals_conceded: u8,\n}\n\nfn build_scores_table(results: &str) -> HashMap<&str, TeamScores> {\n    let mut scores = HashMap::<&str, TeamScores>::new();\n\n    for line in results.lines() {\n        let mut split_iterator = line.split(',');\n        let team_1_name  = split_iterator.next().unwrap();\n        let team_2_name  = split_iterator.next().unwrap();\n        let team_1_score: u8 = split_iterator.next().unwrap().parse().unwrap();\n        let team_2_score: u8 = split_iterator.next().unwrap().parse().unwrap();\n\n        let g1 = TeamScores { goals_scored: team_1_score, goals_conceded: team_2_score };\n        let g2 = TeamScores { goals_scored: team_2_score, goals_conceded: team_1_score };\n\n        scores\n            .entry(team_1_name)\n            .and_modify(|m| {\n                m.goals_scored   += team_1_score;\n                m.goals_conceded += team_2_score;\n            })\n            .or_insert(g1);\n\n        scores\n            .entry(team_2_name)\n            .and_modify(|m| {\n                m.goals_scored   += team_2_score;\n                m.goals_conceded += team_1_score;\n            })\n            .or_insert(g2);\n    }\n\n    scores\n}\n\nfn main() {}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    const RESULTS: &str = \"England,France,4,2\nFrance,Italy,3,1\nPoland,Spain,2,0\nGermany,England,2,1\nEngland,Spain,1,0\";\n\n    #[test]\n    fn build_scores() {\n        let scores = build_scores_table(RESULTS);\n        assert!(\n            [\"England\", \"France\", \"Germany\", \"Italy\", \"Poland\", \"Spain\"]\n                .into_iter()\n                .all(|team_name| scores.contains_key(team_name))\n        );\n    }\n\n    #[test]\n    fn validate_team_score_1() {\n        let scores = build_scores_table(RESULTS);\n        let team = scores.get(\"England\").unwrap();\n        assert_eq!(team.goals_scored, 6);\n        assert_eq!(team.goals_conceded, 4);\n    }\n\n    #[test]\n    fn validate_team_score_2() {\n        let scores = build_scores_table(RESULTS);\n        let team = scores.get(\"Spain\").unwrap();\n        assert_eq!(team.goals_scored, 0);\n        assert_eq!(team.goals_conceded, 3);\n    }\n}\n```\n\n**关键知识点：**\n\n| 知识点                             | 说明                                                         |\n| ---------------------------------- | ------------------------------------------------------------ |\n| `HashMap<K, V>`                    | 键值对集合，键（Key）唯一                                    |\n| `#[derive(Default)]`               | 自动实现 `Default` trait，结构体字段取各自类型的默认值（`u8` 默认为 0） |\n| `struct` 结构体                    | 自定义复合数据类型，`TeamScores { goals_scored, goals_conceded }` |\n| **Entry API**                      | Rust HashMap 的招牌操作，一行搞定\"存在则修改，不存在则插入\"  |\n| `entry().and_modify().or_insert()` | 链式调用：先找 key，找到就执行 `and_modify` 的闭包，找不到就插入 `or_insert` 的值 |\n| `split(',')`                       | 按逗号切分字符串，返回迭代器                                 |\n| `parse::<u8>()`                    | 将 `&str` 解析为 `u8`，返回 `Result`，这里用 `unwrap()` 简单处理 |\n| `lines()`                          | 将字符串按换行符切分，返回每行的迭代器                       |\n| `&str` 作为 HashMap 的 Key         | `&str` 是借用，要注意生命周期——这里 key 借用的数据（`RESULTS` 常量）在整个程序运行期间都存在，所以安全 |\n\n**Entry API 工作流程：**\n\n```\nscores.entry(team_1_name)   // ① 查找 key，返回 Entry 枚举（Occupied 或 Vacant）\n      .and_modify(|m| {     // ② 如果 Occupied（已存在），修改已有值\n          m.goals_scored += ...;\n      })\n      .or_insert(g1);       // ③ 如果 Vacant（不存在），插入新值 g1\n```\n\n> `and_modify` 返回的还是 `Entry`，所以可以继续链式调用 `or_insert`。\n\n---\n\n## 4. 练习四：HashMap 与枚举作为键\n\n> 对应章节：HashMaps（§8.3）、Enums（§6）\n\n**题目：** 水果篮用 HashMap 表示（水果类型 → 数量）。篮中已有 Apple(4)、Mango(2)、Lychee(5)。补充缺失的水果种类（Banana、Pineapple），每种至少 1 个，总数要超过 11。**不能修改已有水果的数量。**\n\n```rust\nuse std::collections::HashMap;\n\n#[derive(Hash, PartialEq, Eq, Debug)]    // 枚举作为 HashMap Key 必须实现这些 trait\nenum Fruit {\n    Apple,\n    Banana,\n    Mango,\n    Lychee,\n    Pineapple,\n}\n\nfn fruit_basket(basket: &mut HashMap<Fruit, u32>) {\n    let fruit_kinds = [\n        Fruit::Apple,\n        Fruit::Banana,\n        Fruit::Mango,\n        Fruit::Lychee,\n        Fruit::Pineapple,\n    ];\n\n    for fruit in fruit_kinds {\n        basket.entry(fruit).or_insert(1);    // 不存在就插入 1，存在则什么也不做\n    }\n}\n\nfn main() {}\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    fn get_fruit_basket() -> HashMap<Fruit, u32> {\n        let content = [(Fruit::Apple, 4), (Fruit::Mango, 2), (Fruit::Lychee, 5)];\n        HashMap::from_iter(content)\n    }\n\n    #[test]\n    fn test_given_fruits_are_not_modified() {\n        let mut basket = get_fruit_basket();\n        fruit_basket(&mut basket);\n        assert_eq!(*basket.get(&Fruit::Apple).unwrap(), 4);\n        assert_eq!(*basket.get(&Fruit::Mango).unwrap(), 2);\n        assert_eq!(*basket.get(&Fruit::Lychee).unwrap(), 5);\n    }\n\n    #[test]\n    fn at_least_five_types_of_fruits() {\n        let mut basket = get_fruit_basket();\n        fruit_basket(&mut basket);\n        assert!(basket.len() >= 5);\n    }\n\n    #[test]\n    fn greater_than_eleven_fruits() {\n        let mut basket = get_fruit_basket();\n        fruit_basket(&mut basket);\n        let count = basket.values().sum::<u32>();\n        assert!(count > 11);\n    }\n}\n```\n\n**关键知识点：**\n\n| 知识点                         | 说明                                                         |\n| ------------------------------ | ------------------------------------------------------------ |\n| 枚举作为 HashMap Key           | 必须实现 `Hash` + `PartialEq` + `Eq` trait（`#[derive]` 一键搞定） |\n| `&mut` 可变借用                | 函数参数 `basket: &mut HashMap<Fruit, u32>` 表示借用可修改的引用 |\n| `entry().or_insert(1)`         | 最简用法：key 存在就不动，不存在就插入 1                     |\n| `#[derive]` 属性宏             | 自动为自定义类型实现标准 trait（`Debug`、`Clone`、`Hash`、`Default` 等） |\n| `HashMap::from_iter()`         | 从迭代器构造 HashMap                                         |\n| `basket.values().sum::<u32>()` | 消费 values 迭代器，求和。`::<u32>` 是 turbofish 语法指定类型 |\n\n---\n\n## 5. 核心知识点总结\n\n### 5.1 `if/else` 是表达式\n\n```rust\n// Rust：if 直接返回值\nlet price = if n > 40 { n } else { n * 2 };\n\n// C 语言等效写法（三目运算符）：\n// int price = n > 40 ? n : n * 2;\n```\n\n### 5.2 `match` 模式匹配\n\n```rust\nmatch value {\n    Command::Uppercase    => /* 处理逻辑 */,\n    Command::Trim         => /* 处理逻辑 */,\n    Command::Append(n)    => /* n 绑定到内部数据 */,\n}\n// match 必须穷尽所有可能（exhaustive），否则编译报错\n```\n\n### 5.3 所有权（Ownership）基础\n\n| 类型                                            | 特性                                              |\n| ----------------------------------------------- | ------------------------------------------------- |\n| `i32`、`u8`、`bool`、`char` 等基本类型          | 实现了 `Copy` trait，赋值时自动复制，原变量仍可用 |\n| `String`、`Vec<T>`、`HashMap<K,V>` 等堆分配类型 | 未实现 `Copy`，赋值时**转移所有权**，原变量失效   |\n| `&T`（引用）                                    | **借用**，不转移所有权，原变量仍可用              |\n\n```rust\nlet a = 42;\nlet b = a;      // a 是 i32，实现了 Copy，所以 a 仍然可用\n\nlet s1 = String::from(\"hello\");\nlet s2 = s1;    // s1 所有权转移给 s2，s1 不再可用！\n// println!(\"{}\", s1);  // ❌ 编译错误：s1 已被移动\n```\n\n### 5.4 HashMap Entry API\n\n```rust\n// 模式一：存在则修改，不存在则插入\nmap.entry(key)\n   .and_modify(|v| *v += 1)\n   .or_insert(0);\n\n// 模式二：只保证键存在（不存在就插入默认值）\nmap.entry(key).or_insert(default_value);\n\n// 模式三：存在就返回可变引用，不存在就插入并返回\nlet v = map.entry(key).or_insert_with(|| expensive_computation());\n```\n\n### 5.5 模块系统速查\n\n```rust\nmod my_module {           // 定义模块\n    pub fn public_fn() {} // pub = 公开\n    fn private_fn() {}    // 默认私有\n}\n\nuse crate::my_module::public_fn;  // 导入（绝对路径）\nuse super::SomeType;              // 导入父模块的类型\nuse std::collections::HashMap;    // 导入标准库类型\n```\n\n---\n\n## 6. Rustlings 练习与《Rust Book》章节对照\n\n| 练习            | 《Rust Book》章节 |\n| --------------- | ----------------- |\n| variables       | §3.1              |\n| functions       | §3.3              |\n| if              | §3.5              |\n| primitive_types | §3.2, §4.3        |\n| vecs            | §8.1              |\n| move_semantics  | §4.1-2            |\n| structs         | §5.1, §5.3        |\n| enums           | §6, §18.3         |\n| strings         | §8.2              |\n| modules         | §7                |\n| hashmaps        | §8.3              |\n| options         | §10.1             |\n| error_handling  | §9                |\n| generics        | §10               |\n| traits          | §10.2             |\n| lifetimes       | §10.3             |\n| tests           | §11.1             |\n| iterators       | §13.2-4           |\n| smart_pointers  | §15, §16.3        |\n| threads         | §16.1-3           |\n| macros          | §20.5             |\n| clippy          | Appendix D        |\n| conversions     | n/a               |\n\n---\n\n## 7. 学习进度\n\n当前进度：已完成变量、函数、条件判断、基本类型、Vec、移动语义、结构体、枚举、字符串、模块、HashMap，**正在做 options 相关练习**。\n\n",
            "url": "https://kyriechao.github.io/posts/2026/07/28-rust-day3",
            "title": "Rust 学习计划",
            "summary": "Rust 第三天学习",
            "date_modified": "2026-07-28T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/27-diary",
            "content_html": "\n# 2026-07-27 19:13:23 +0800\n\n## 技术\n今天继续学 Rust，笔记整理在 [这里](27-rust-day2.md)。\n\n## 生活\n培训正式开始第二周了。因为有自学基础，今天的课程内容对我来说更像是复习巩固。明天进入 C 语言新手教学，希望能有些新的挑战。\n \n节奏方面，第一个月整体偏慢，好处是有时间消化和动手实践，坏处是进度感和自学时有落差。还在适应中。\n\n## 明日计划\n明天继续学习Rust。\n\n- Trait 初探（第 15 课）：trait 定义、impl Trait for Type、泛型约束 <T: Trait>\n- Rustlings 实战：用已学语法刷题\n- 尝试第 3 题（自引用结构体）的索引方案：用 usize 范围替代 &str",
            "url": "https://kyriechao.github.io/posts/2026/07/27-diary",
            "title": "日志",
            "summary": "Rust 第二天学习",
            "date_modified": "2026-07-27T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/27-rust-day2",
            "content_html": "\n# Rust 基础语法（Day 2）\n\n## 一、Hello World 与环境搭建\n\n### 1.1 第一个程序\n\n```rust\n// 作业 1：打印姓名年龄\nfn main(){\n    println!(\"陈鸽涛\");\n    println!(\"2005年 21岁\");\n}\n```\n\n**`println!` 是宏不是函数。** 忘记写 `!` 时的编译错误：\n\n```\nerror[E0423]: expected function, found macro `println`\nhelp: use `!` to invoke the macro\n```\n\n**知识点——宏 vs 函数：**\n\n| 对比项   | 函数           | 宏                                   |\n| -------- | -------------- | ------------------------------------ |\n| 调用方式 | `func(args)`   | `macro!(args)`                       |\n| 参数数量 | 固定           | 可变（如 `println!(\"{}\", a, b, c)`） |\n| 编译方式 | 运行时调用     | 编译期展开为代码                     |\n| 典型例子 | `fn add(x, y)` | `println!`、`vec!`、`format!`        |\n\nRust 宏比 C 的 `#define` 强大得多——它是**语法层面的代码生成**，可以做模式匹配、递归展开等。\n\n---\n\n## 二、变量与不可变性\n\n```rust\nfn main(){\n    let name = \"陈鸽涛\";\n    const MAX_SCORE:i32 = 100;\n    let mut count = 0;\n    println!(\"Hello, {}!\", name);\n    println!(\"Max score is {}\", MAX_SCORE);\n    println!(\"Count:{}\",count);\n    count += 1;\n    println!(\"Count:{}\",count);\n    count += 1;\n    println!(\"Count:{}\",count);\n}\n```\n\n尝试修改不可变变量时的报错：\n\n```\nerror[E0384]: cannot assign twice to immutable variable `name`\nhelp: consider making this binding mutable\n```\n\n### 知识点——`let` vs `let mut` vs `const`\n\n| 特性       | `let`              | `let mut`    | `const`                        |\n| ---------- | ------------------ | ------------ | ------------------------------ |\n| 可变性     | 不可变（默认）     | 可变         | 永远不可变                     |\n| 类型注解   | 可省略（类型推断） | 可省略       | **必须**显式标注               |\n| 作用域     | 任意作用域         | 任意作用域   | 全局/模块级                    |\n| 运行时计算 | 支持               | 支持         | 不支持，必须是编译期常量表达式 |\n| 命名惯例   | `snake_case`       | `snake_case` | `SCREAMING_SNAKE_CASE`         |\n\n> Rust 默认不可变的设计哲学：编译期就能确定哪些值会变、哪些不会，从而做更精确的借用检查。这跟函数式编程的\"不可变数据\"理念一致。\n\n---\n\n## 三、基本数据类型\n\n```rust\nfn main(){\n    let tup:(i32,f64,bool) = (21, 160.0, true);\n    let arr = [1,2,3,4,5];\n    println!(\"年龄：{}\",tup.0);\n    println!(\"身高：{}\",tup.1);\n    println!(\"是学生么：{}\",tup.2);\n    println!(\"数组的第3个元素：{}\",arr[2]);\n    println!(\"数组越界：{}\",arr[10]);\n}\n```\n\n越界访问的编译期警告：\n\n```\nerror: this operation will panic at runtime\nindex out of bounds: the length is 5 but the index is 10\n```\n\n### 知识点\n\n#### 标量类型（Scalar Types）\n\n| 类型 | 示例               | 说明                                                         |\n| ---- | ------------------ | ------------------------------------------------------------ |\n| 整数 | `i8` `u32` `isize` | 有符号 `i` / 无符号 `u`，位宽 8/16/32/64/128，`size` 跟平台指针同宽 |\n| 浮点 | `f32` `f64`        | 默认 `f64`（IEEE 754）                                       |\n| 布尔 | `bool`             | `true` / `false`                                             |\n| 字符 | `char`             | 4 字节，存 Unicode 标量值（不是 ASCII）                      |\n\n#### 复合类型\n\n**元组（Tuple）**：固定长度，每个位置类型可不同，用 `.索引` 访问。\n\n```rust\nlet tup: (i32, f64, bool) = (21, 160.0, true);\nprintln!(\"{}\", tup.0);  // 21\n// 也可解构：let (age, height, is_student) = tup;\n```\n\n**数组（Array）**：固定长度，所有元素同类型，分配在**栈**上。\n\n```rust\nlet arr: [i32; 5] = [1, 2, 3, 4, 5];\nlet arr2 = [0; 100];  // 100 个 0\n```\n\n数组越界：Rust 在**运行时**做边界检查，越界会 **panic**（而不是像 C 那样读到脏数据）。这是安全性的体现——宁愿崩溃也不允许内存不安全操作。\n\n> 变长数组用 `Vec<T>`（第 11 课），在堆上分配。\n\n---\n\n## 四、函数\n\n```rust\n// 课堂代码\nfn main(){\n    say_hello();\n    let result = add(5, 3);\n    println!(\"5 + 3 = {}\", result);\n}\n\nfn say_hello() {\n    println!(\"Hello, world!\");\n}\n\nfn add(a: i32, b: i32) -> i32 {\n    a + b\n}\n```\n\n```rust\n// 作业：multiply / is_even / calculate\nfn main(){\n    println!(\"3*5 = {}\", multiply(3, 5));\n    println!(\"Is 4 even? {}\", is_even(4));\n    println!(\"Calculate (3+5)*2 = {}\", calculate(3,5));\n}\n\nfn multiply(a: i32, b: i32) -> i32 {\n    a * b\n}\nfn is_even(n: i32) -> bool {\n    n % 2 == 0\n}\n\nfn calculate(a:i32,b:i32)->i32{\n    (a+b)*2\n}\n```\n\n### 知识点——语句 vs 表达式\n\n这是 Rust 非常核心的概念：\n\n| 概念                     | 特点                                | 示例                                |\n| ------------------------ | ----------------------------------- | ----------------------------------- |\n| **语句（Statement）**    | 执行操作，**不返回值**，以 `;` 结尾 | `let x = 5;`                        |\n| **表达式（Expression）** | 计算并**返回值**，不以 `;` 结尾     | `a + b`、`if true { 1 } else { 0 }` |\n\n```rust\nfn add(a: i32, b: i32) -> i32 {\n    a + b          // 表达式，不加分号 = 返回值\n    // 等价于 return a + b;\n}\n\nfn add_wrong(a: i32, b: i32) -> i32 {\n    a + b;         // 加了分号 → 语句 → 返回 () → 类型不匹配！\n}\n```\n\n> `return` 关键字用于**提前返回**，最后一个表达式的值是默认返回值（更符合 Rust 惯用风格）。\n\n### 函数签名\n\n```rust\nfn 函数名(参数: 类型) -> 返回类型 { 函数体 }\n```\n\n- 参数**必须**标注类型（不支持类型推断）。\n- `->` 后面是返回类型，若函数无返回值则可省略（实际返回 `()`，即 unit 类型）。\n\n---\n\n## 五、控制流\n\n运行结果存档：\n\n```\n年龄为20，状态为成年\nn = 10\nn = 9\n...\nn = 1\n1到100的和为5050\n```\n\n### 知识点\n\n#### `if` 是表达式\n\n```rust\nlet status = if age >= 18 {\n    \"成年\"\n} else {\n    \"未成年\"\n};  // 注意这里有分号，因为这是 let 语句\n// 两个分支必须返回相同类型\n```\n\n与 C/Java 不同，Rust 中 `if` 可以有返回值。三元运算 `x ? a : b` 在 Rust 中直接用 `if` 表达即可。\n\n#### 三种循环\n\n| 循环    | 语法                       | 适用场景                             |\n| ------- | -------------------------- | ------------------------------------ |\n| `loop`  | `loop { ... }`             | 无限循环，需手动 `break`；可带返回值 |\n| `while` | `while cond { ... }`       | 条件循环                             |\n| `for`   | `for item in iter { ... }` | 遍历集合（最常用、最安全）           |\n\n```rust\n// loop 带返回值\nlet result = loop {\n    counter += 1;\n    if counter == 10 {\n        break counter * 2;  // break 后面跟值 = loop 的返回值\n    }\n};  // result = 20\n```\n\n> 能用 `for` 就不要用 `while` + 手动索引——编译器会对 `for` 做更多优化，且消除了越界风险。\n\n---\n\n## 六、所有权（Ownership）—— Rust 最核心的概念\n\n### 6.1 Move 语义\n\n```rust\n// Move 报错实验\n// error[E0382]: borrow of moved value: `s`\n// move occurs because `s` has type `String`, which does not implement the `Copy` trait\n```\n\n修复：使用 `.clone()` 做深拷贝\n\n```rust\nfn main() {\n    let s = String::from(\"test\");\n    let t = s.clone();\n    println!(\"s:{} t:{}\", s, t);\n}\n```\n\n### 知识点——所有权三规则\n\n1. Rust 中每一个值都有一个**所有者（Owner）**。\n2. 同一时刻**只能有一个所有者**。\n3. 当所有者离开作用域，值会被**自动释放**（调用 `drop`）。\n\n```rust\nlet s1 = String::from(\"hello\");\nlet s2 = s1;          // s1 的所有权转移给了 s2（Move）\n// println!(\"{}\", s1); // 编译错误！s1 已经失效\n```\n\n### 栈上数据 vs 堆上数据——Move 与 Copy 的本质区别\n\n这就是你表中\"从背规则到理解栈/堆差异\"的关键：\n\n| 数据位置                                     | 特性                       | 赋值行为                         | 实现了        |\n| -------------------------------------------- | -------------------------- | -------------------------------- | ------------- |\n| **栈**（整数、布尔、char、元组含 Copy 类型） | 大小编译期确定，拷贝成本低 | `let b = a;` 后 `a` 仍然可用     | `Copy` trait  |\n| **堆**（String、Vec 等）                     | 大小运行时确定，拷贝成本高 | `let b = a;` 后 `a` 失效（Move） | 未实现 `Copy` |\n\n```rust\nlet x = 5;\nlet y = x;    // Copy，x 仍可用\nprintln!(\"{}\", x);  // OK\n\nlet s1 = String::from(\"hello\");\nlet s2 = s1;   // Move，s1 失效\n// println!(\"{}\", s1);  // error[E0382]\n```\n\n**为什么堆数据不自动 Copy？**——`String` 内部是一个指针指向堆内存。如果自动 Copy 指针（浅拷贝），两个变量指向同一块堆内存，离开作用域时会发生**双重释放（Double Free）**。Rust 用 Move 语义从根本上杜绝了这个问题。\n\n---\n\n## 七、引用与借用（References & Borrowing）\n\n报错存档：\n\n```\nerror[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable\n```\n\n### 知识点——引用规则\n\n| 引用类型   | 写法     | 权限     |    同时存在数量    |\n| ---------- | -------- | -------- | :----------------: |\n| 不可变引用 | `&T`     | 只读     |       无限制       |\n| 可变引用   | `&mut T` | 可读可写 | **同时只能有一个** |\n\n**核心规则（两条）：**\n\n1. 在任意给定时间，**要么**只能有一个可变引用，**要么**只能有任意数量的不可变引用，两者不能同时存在。\n2. 引用必须始终有效（生命周期保证，见后文）。\n\n```rust\nlet mut s = String::from(\"hello\");\n\nlet r1 = &s;  // 不可变引用，OK\nlet r2 = &s;  // 不可变引用，OK\n// let r3 = &mut s;  // 错误！已有不可变引用存在\n\nprintln!(\"{} {}\", r1, r2);  // r1, r2 最后使用位置\n// 此后 r1, r2 不再使用，可以创建可变引用了\n\nlet r3 = &mut s;  // OK，因为 r1, r2 已经\"失效\"\n```\n\n> 这条规则在编译期杜绝了**数据竞争（Data Race）**——两个指针同时访问同一数据、其中一个是写操作。不需要 GC，不需要锁，编译期就保证了线程安全的前提条件。\n\n---\n\n## 八、结构体（Struct）\n\n```rust\nstruct Rentangle {\n    width: u32,\n    height: u32,\n}\n\nfn area(r:&Rentangle) -> u32{\n    r.height * r.width\n}\nfn main() {\n    let r1 = Rentangle{\n        width:10,\n        height:20,\n    };\n    let r2 = Rentangle{\n        width:10,\n        height:10,\n    };\n    println!(\"{}\",area(&r1));\n    println!(\"{}\",area(&r2));\n}\n```\n\n### 知识点\n\n#### 三种结构体\n\n```rust\n// 1. 具名字段结构体（最常用）\nstruct User {\n    name: String,\n    age: u32,\n}\n\n// 2. 元组结构体（字段无名称，通过索引访问）\nstruct Color(u8, u8, u8);\nlet black = Color(0, 0, 0);\nprintln!(\"{}\", black.0);\n\n// 3. 单元结构体（无字段，用作标记类型）\nstruct AlwaysEqual;\n```\n\n#### `impl` 块——给结构体添加方法\n\n```rust\nimpl Rentangle {\n    fn area(&self) -> u32 {\n        self.width * self.height\n    }\n\n    fn can_hold(&self, other: &Rentangle) -> bool {\n        self.width > other.width && self.height > other.height\n    }\n\n    // 关联函数（没有 self，相当于静态方法）\n    fn square(size: u32) -> Rentangle {\n        Rentangle { width: size, height: size }\n    }\n}\n\n// 调用\nlet r = Rentangle { width: 10, height: 20 };\nr.area();              // 方法调用\nRentangle::square(5);  // 关联函数调用\n```\n\n> `&self` 是 `self: &Self` 的语法糖。`self` 的所有权形式分三种：`&self`（不可变借用）、`&mut self`（可变借用）、`self`（获取所有权，极少用）。\n\n---\n\n## 九、枚举与模式匹配（Enum & Pattern Matching）\n\n```rust\nenum Shape {\n    Retangle { width: f64, height: f64 },\n    Cricle(f64),\n    Triangle(f64, f64, f64),\n}\n\nfn calculate_area(s: &Shape) -> f64 {\n    match s {\n        Shape::Cricle(r) =>{\n            3.14*r*r\n        },\n        Shape::Retangle { width, height } =>{\n            width * height\n        },\n        Shape::Triangle(a, b,c) =>{\n            let p = (a + b + c) / 2.0;\n            (p * (p - a) * (p - b) * (p - c)).sqrt()\n        },\n    }\n}\n\nfn main() {\n    let s1 = Shape::Cricle(4.0);\n    let s2 = Shape::Retangle { width: 10.0, height: 20.0 };\n    let s3 = Shape::Triangle(3.0, 4.0, 5.0);\n\n    println!(\"{}\",calculate_area(&s1));\n    println!(\"{}\",calculate_area(&s2));\n    println!(\"{}\",calculate_area(&s3));\n\n    let n :Option<i32> = Some(5);\n\n    if let Some(5) = n {\n        println!(\"is 5\");\n    }\n}\n```\n\n### 知识点\n\n#### `match` 的穷尽性\n\n编译器强制你处理所有可能的枚举变体，否则编译报错。这是 Rust 消除空指针/未处理分支的关键机制。\n\n#### `Option<T>`——Rust 没有 null\n\n```rust\nenum Option<T> {\n    Some(T),  // 有值\n    None,     // 无值\n}\n```\n\n`Option` 被自动引入作用域（无需 `use`），变体 `Some` 和 `None` 也可直接使用。任何可能\"没有值\"的情况都用 `Option` 表达，编译期强制处理，避免了 null 引起的运行时崩溃。\n\n#### `if let`——简洁的单分支 match\n\n```rust\n// match 写法\nmatch n {\n    Some(5) => println!(\"is 5\"),\n    _ => (),  // 什么都不做\n}\n\n// if let 写法（更简洁）\nif let Some(5) = n {\n    println!(\"is 5\");\n}\n```\n\n`if let` 是 `match` 的语法糖，当你只关心一个分支时使用。还可以加 `else`：\n\n```rust\nif let Some(x) = n {\n    println!(\"值为 {}\", x);\n} else {\n    println!(\"没有值\");\n}\n```\n\n---\n\n## 十、错误处理\n\n```rust\nuse std::io;\n\nfn divide(f1: f64, f2: f64) -> Result<f64, String> {\n    if f2 == 0.0 {\n        Err(\"除数不能为零\".to_string())\n    } else {\n        Ok(f1 / f2)\n    }\n}\n\nfn divide_err(f1: f64, f2: f64) -> Result<f64, io::Error> {\n    if f2 == 0.0 {\n        Err(io::Error::new(io::ErrorKind::InvalidInput, \"不能除以零\"))?\n    }\n    Ok(f1 / f2)\n}\n\nfn main() -> Result<(), Box<dyn std::error::Error>> {\n    let r = divide(4.0, 0.0);\n\n    match r {\n        Ok(f) => println!(\"{}\", f),\n        Err(s) => println!(\"{}\", s),\n    }\n\n    let result = divide_err(10.0, 2.0)?;\n    println!(\"divide_err 计算结果: {}\", result);\n\n    let _result2 = divide_err(4.0, 0.0)?;\n\n    Ok(())\n}\n```\n\n### 补做练习：`parse_and_double`（验证 `?` 理解）\n\n```rust\nfn parse_number(s: &str) -> Result<i32, String> {\n    let o = s.parse::<i32>();\n    match o {\n        Ok(v) => Ok(v),\n        Err(_) => Err(\"无法解析:{s}\".to_string()),\n    }\n}\n\nfn parse_and_double(s: &str) -> Result<i32, String> {\n    let o = parse_number(s)?;\n    Ok(o*2)\n}\n\nfn main() {\n    match parse_and_double(\"10\") {\n        Ok(n) => println!(\"结果: {}\", n),\n        Err(e) => println!(\"错误: {}\", e),\n    }\n\n    match parse_and_double(\"abc\") {\n        Ok(n) => println!(\"结果: {}\", n),\n        Err(e) => println!(\"错误: {}\", e),\n    }\n}\n```\n\n### 知识点\n\n#### `Result<T, E>`——可恢复错误\n\n```rust\nenum Result<T, E> {\n    Ok(T),   // 成功，包含返回值\n    Err(E),  // 失败，包含错误信息\n}\n```\n\n#### 三种处理方式对比\n\n| 方式                    | 语法                                      | 行为                              | 适用场景                       |\n| ----------------------- | ----------------------------------------- | --------------------------------- | ------------------------------ |\n| `match`                 | `match r { Ok(v) => ..., Err(e) => ... }` | 分支处理                          | 需要针对不同结果做不同逻辑     |\n| `unwrap()` / `expect()` | `r.expect(\"msg\")`                         | 成功取值，失败 **panic**          | 原型阶段、测试、确定不会失败时 |\n| `?` 运算符              | `r?`                                      | 成功取值，失败**提前 return Err** | 传播错误的惯用写法             |\n\n#### `?` 运算符展开机制\n\n```rust\nlet value = some_result?;\n\n// 等价于：\nlet value = match some_result {\n    Ok(v) => v,\n    Err(e) => return Err(e.into()),  // .into() 会把错误类型转为调用者声明的错误类型\n};\n```\n\n**前提条件**：使用 `?` 的函数返回类型必须是 `Result` 或 `Option`，且错误类型要兼容。\n\n#### `Box<dyn std::error::Error>` 的作用\n\n在 `main` 返回 `Result<(), Box<dyn Error>>` 时，`?` 可以把任意类型的错误转为 `Box<dyn Error>`（因为所有标准错误类型都实现了 `Error` trait）。`Box<dyn Error>` 相当于\"能装任何错误类型的盒子\"，牺牲了一点运行时性能换取便利。\n\n- `Box` → 堆上分配（因为 trait 对象大小编译期不确定）\n- `dyn Error` → 动态分发的 Error trait 对象\n- 具体原理在 Trait 章节会深入讲\n\n---\n\n## 十一、集合类型（一）：Vector\n\n```rust\nfn sum_vec(v: &Vec<i32>) -> i32 {\n    let mut sum = 0;\n    for i in v {\n        sum += i;\n    }\n    return sum;\n}\n\nfn remove_even(v: &mut Vec<i32>) {\n    let mut i = 0;\n    while i < v.len() {\n        if v[i] % 2 == 0 {\n            v.remove(i);\n        } else {\n            i += 1;\n        }\n    }\n}\n\nfn remove_even_to(v: &mut Vec<i32>) {\n    v.retain(|&x| x % 2 != 0);\n}\n\nfn main() {\n    let mut v1 = vec![10, 20, 30, 40, 50];\n    let mut v2 = vec![10, 20, 30, 40, 50];\n    \n    for i in &v1 { print!(\"{} \", i); }\n    println!();\n    for i in &v2 { print!(\"{} \", i); }\n    println!();\n    println!(\"{}\", sum_vec(&v1));\n\n    remove_even(&mut v1);\n    for i in &v1 { println!(\"{}\", i); }\n\n    remove_even_to(&mut v2);\n    for i in &v2 { print!(\"{} \", i); }\n\n    // 编译错误实验\n    let mut v = vec![1,2,3];\n    for i in &v{\n        v.push(*i);  // 同时持有不可变引用和可变引用 → 编译报错\n    }\n}\n```\n\n报错存档：\n\n```\nerror[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable\n```\n\n### 知识点\n\n#### `Vec<T>` 基础操作\n\n```rust\nlet mut v: Vec<i32> = Vec::new();  // 创建空 Vec\nlet v2 = vec![1, 2, 3];            // vec! 宏创建\nv.push(4);                          // 追加元素\nlet last = v.pop();                 // 弹出最后一个元素，返回 Option<T>\nlet third = v[2];                   // 索引访问（越界会 panic）\nlet third = v.get(2);               // 返回 Option<&T>（越界返回 None）\n```\n\n#### 删除元素的三种方式及借用规则\n\n你在代码中对 `for` 循环中同时读写 Vec 的实验，恰好验证了借用规则：**在不可变引用的生命周期内，不能同时存在可变引用**。`for i in &v` 对 `v` 创建了不可变引用，循环体内不能再 `push` 或 `remove`。\n\n#### `remove` vs `retain`\n\n- `remove(index)` — 删除指定索引元素，后续元素前移，O(n)。\n- `retain(|&x| condition)` — 保留满足条件的元素，一次遍历完成，更高效也是 Rust 惯用风格。\n\n---\n\n## 十二、集合类型（二）：HashMap\n\n```rust\nfn get_age(map: &HashMap<String, i32>, name: &str) -> Option<i32> {\n    for (key, value) in map {\n        if key == (name) {\n            return Some(*value);\n        }\n    }\n    None\n}\n\nfn main() {\n    let mut scores = HashMap::new();\n\n    scores.insert(String::from(\"Tom\"), 10);\n    scores.insert(String::from(\"Mike\"), 21);\n    scores.insert(String::from(\"Hong\"), 30);\n\n    for (key, value) in &scores {\n        println!(\"{} {}\", key, value);\n    }\n\n    let m = get_age(&scores, \"Mike\");\n\n    if let Some(age) = m {\n        println!(\"存在 {}\",age);\n    } else {\n        println!(\"不存在\");\n    }\n\n    *scores.entry(String::from(\"Mike\")).or_insert(0) += 10;\n\n    for (key, value) in &scores {\n        println!(\"{} {}\", key, value);\n    }\n}\n```\n\n### 知识点\n\n#### `HashMap` 基础\n\n```rust\nuse std::collections::HashMap;\n\nlet mut map = HashMap::new();\nmap.insert(key, value);               // 插入或覆盖\nlet v = map.get(&key);                // 返回 Option<&V>\nmap.remove(&key);                     // 删除\n\n// 遍历\nfor (k, v) in &map { ... }\n```\n\n#### `entry` API——HashMap 的核心技巧\n\n```rust\n*scores.entry(String::from(\"Mike\")).or_insert(0) += 10;\n```\n\n拆解这一步：\n\n| 步骤 | 方法/操作       | 作用                                                    |\n| ---- | --------------- | ------------------------------------------------------- |\n| 1    | `.entry(key)`   | 获取 `key` 对应的 `Entry` 枚举（`Occupied` / `Vacant`） |\n| 2    | `.or_insert(0)` | 如果不存在就插入 `0`，返回 `&mut V`（可变引用）         |\n| 3    | `*... += 10`    | 解引用后原地修改值                                      |\n\n```rust\n// entry 的三种处理方式\nmap.entry(key).or_insert(default);    // 不存在时插入默认值\nmap.entry(key).and_modify(|v| *v += 1).or_insert(1);  // 存在则修改，不存在则插入\n```\n\n> `entry` API 只做一次哈希查找，比\"先 `get` 检查再 `insert`\"高效且更符合 Rust 所有权模型。\n\n#### HashMap 与所有权\n\n```rust\nmap.insert(String::from(\"Tom\"), 10);\n// String 的所有权被移入 HashMap\n// 此后不能再使用原来的 String 变量\n```\n\n对于实现了 `Copy` 的类型（如 `i32`），值会被拷贝而非移动。所以你的代码中 `10`、`21` 这些值插入后仍可使用。\n\n---\n\n## 十三、字符串深入\n\n```rust\nfn greet(name: &str) {\n    println!(\"你好，{name}\");\n}\n\nfn main() {\n    let s1 = String::from(\"hello\");\n    let s2 = \"World\";\n\n    greet(&s1);\n    greet(s2);\n\n    let s3 = String::from(\"Rust\");\n    let s4 = String::from(\"很好\");\n\n    let s5 = format!(\"{} {}\", s3, s4);\n    println!(\"{s5}\");\n    println!(\"{} {}\", s3, s4);\n\n    let s6 = String::from(\"安全地获取第一个字符。\").chars().nth(0);\n\n    if let Some(ch) = s6 {\n        println!(\"{}\", ch);\n    } else {\n        println!(\"取值失败.\");\n    }\n}\n```\n\n### 知识点\n\n#### `String` vs `&str`\n\n| 类型     | 存储位置           | 可变性 | 所有权   | 用途                               |\n| -------- | ------------------ | :----: | -------- | ---------------------------------- |\n| `String` | 堆上分配           |  可变  | 拥有数据 | 需要修改、动态构建的字符串         |\n| `&str`   | 指向已有数据的引用 | 不可变 | 借用     | 只读访问字符串数据（函数参数首选） |\n\n```rust\nlet s = String::from(\"hello\");  // String，堆上分配\nlet slice: &str = &s;           // &str，借用 String 的内容\nlet literal: &str = \"hello\";    // &str，字面量直接嵌入二进制\n```\n\n**函数参数选 `&str` 的好处**——调用者既可以传 `&String`（自动解引用强制转换，Deref Coercion），也可以传 `&str` 字面量，最灵活。\n\n#### `format!` 宏——不获取所有权的拼接\n\n```rust\nlet s5 = format!(\"{} {}\", s3, s4);\nprintln!(\"{} {}\", s3, s4);  // s3, s4 依然可用！\n```\n\n`format!` 只借用参数，不取所有权。对比 `+` 拼接会消耗左侧 String 的所有权：\n\n```rust\nlet s = s1 + &s2;  // s1 所有权被消耗，s1 不可再用\n```\n\n#### 访问字符串中的字符\n\n```rust\n// 不能直接索引：s[0] ❌（因为 UTF-8 字节不等同于字符）\ns.chars().nth(0);  // ✅ 返回 Option<char>，遍历 Unicode 标量\n```\n\nRust 不允许 `s[0]` 直接索引，因为 UTF-8 编码下一个字符可能占 1-4 个字节，`s[0]` 拿到的是字节而非完整字符。`.chars()` 和 `.bytes()` 分别按字符和字节迭代，语义明确。\n\n---\n\n## 十四、生命周期（Lifetime）\n\n闭卷测试通过的代码：\n\n```rust\nfn longest<'a>(x: &'a str, y: &'a str) -> &'a str {\n    if x.len() > y.len() { x } else { y }\n}\n```\n\n### 知识点\n\n#### 为什么需要生命周期标注？\n\n考虑下面这个函数（编译不通过）：\n\n```rust\nfn longest(x: &str, y: &str) -> &str {\n    if x.len() > y.len() { x } else { y }\n}\n```\n\n编译器不知道返回值借用了 `x` 还是 `y`，无法推断返回引用的有效期。**生命周期标注不会改变引用的实际存活时间**，只是告诉编译器多个引用之间的关系。\n\n#### 生命周期语法\n\n```rust\n&'a T          // 带生命周期标注的引用\nfn<'a>         // 声明生命周期参数（类似泛型声明）\n```\n\n`'a` 读作\"生命周期 a\"，命名惯例使用短小写字母（`'a`、`'b`、`'c`）。\n\n#### 三条生命周期省略规则（方便记忆）\n\n编译器在以下三种情况下可以自动推断生命周期，不需要手动标注：\n\n1. 每个引用参数都有各自的生命周期：`fn foo(x: &T)` → `fn foo<'a>(x: &'a T)`\n2. 如果只有一个输入生命周期参数，它被赋予所有输出生命周期\n3. 如果有多个输入参数，但其中一个是 `&self` 或 `&mut self`，则 `self` 的生命周期赋予所有输出\n\n**当三条规则都不满足时，必须手动标注。**\n\n你的 `longest` 有两个输入引用、没有 `self`，不符合第 2、3 条，所以必须标注。\n\n---\n\n## 十五、`?` 手动展开\n\n```rust\n// 初版（有编译错误）\nfn combine_manual() -> Result<i32, String> {\n    let a = step1();\n    match a {\n        Ok(k) => k,\n        Err(e) => return Err(e),\n    };\n    let b = step2();\n    match b {\n        Ok(k) => k,\n        Err(e) => return Err(e),\n    };\n}\n```\n\n### `?` 展开的完整对应\n\n```rust\n// 使用 ? 的版本\nfn combine() -> Result<i32, String> {\n    let a = step1()?;\n    let b = step2()?;\n    Ok(a + b)\n}\n\n// 手动展开（修复后）\nfn combine_manual() -> Result<i32, String> {\n    let a = match step1() {\n        Ok(k) => k,\n        Err(e) => return Err(e),\n    };\n    let b = match step2() {\n        Ok(k) => k,\n        Err(e) => return Err(e),\n    };\n    Ok(a + b)\n}\n```\n\n**注意点：**\n\n- `match` 分支后不加分号（它是一个表达式）。\n- `let a = match { ... };` 整个 `let` 语句最后要加分号。\n- `?` 展开时还会调用 `.into()` 做错误类型转换（这个在 Trait 章节会理解得更透彻）。\n\n---\n\n## 十六、知识汇总\n\n| 概念                          | 状态 | 说明                                         |\n| ----------------------------- | :--: | -------------------------------------------- |\n| 所有权三规则                  |  ✅   | 一个所有者、Move 语义、离开作用域自动释放    |\n| Move vs Copy                  |  ✅   | 关键区别在于栈数据（Copy）vs 堆数据（Move）  |\n| 引用与借用规则                |  ✅   | 同时只能有多个不可变引用 OR 一个可变引用     |\n| 生命周期标注 `'a`             |  ✅   | 能写出 `longest` 函数                        |\n| `?` 展开机制                  |  ✅   | 理解是 `match + return Err` 的语法糖         |\n| `match` / `if let` / `Option` |  ✅   | 穷尽性检查，消除 null                        |\n| `Result` 三种处理方式         |  ✅   | `match` / `expect` / `?`                     |\n| `Vec` 增删查改                |  ✅   | `push` / `pop` / `get` / `remove` / `retain` |\n| `HashMap` entry API           |  ✅   | 一次哈希查找完成\"存在则改、不存在则插\"       |\n| Trait / 泛型约束              |  ❌   | 尚未触及（下一课内容）                       |\n| `Box<dyn Error>` 原理         |  ⚠️   | 知道用法，Trait 对象和动态分发原理待深入     |\n| `impl` 块与关联函数           |  ⚠️   | 会用，但面向对象风格的更多模式待补充         |\n\n---\n\n## 十七、课后练习\n\n1. 编写一个函数 `fibonacci(n: u32) -> u64`，返回第 n 个斐波那契数（提示：用 `match` 或 `if` 处理 n=0, n=1 的基础情况）。\n2. 用 `impl` 为你的 `Rentangle` 结构体添加 `area` 和 `can_hold` 方法（不用外部函数），体会 `self` 引用的用法。\n3. 实现一个函数 `first_word(s: &str) -> &str`，返回字符串的第一个单词（空格分隔），并用不同输入测试生命周期是否正常工作。\n4. 用 `HashMap` 实现一个简单的词频统计器：给定一段话，统计每个单词出现的次数（提示：用 `split_whitespace()` + `entry` API）。\n5. （选做）阅读 `std::option::Option` 和 `std::result::Result` 的官方文档，看看它们分别有哪些常用方法（`map`、`and_then`、`unwrap_or` 等），尝试在你的代码中替换掉部分 `match`。",
            "url": "https://kyriechao.github.io/posts/2026/07/27-rust-day2",
            "title": "Rust 学习计划",
            "summary": "Rust 第二天学习",
            "date_modified": "2026-07-27T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/25-diary",
            "content_html": "\n# 2026-07-25\n\n## 技术\n\n今天做了些 Rust 的练习，顺便新建了「日志」和「Rust 学习」两个博客系列。希望这两个系列能越写越长。\n\n两个系列各自的第一篇文档也写好了。打算待到 18 点多，再用 Rust 写点能解决实际问题的代码，顺便也练练 C。\n\n## 生活\n\n又过了一个平淡的日子。来培训机构快一个月了，说实话，感觉还没学到什么东西。接下来也不想硬撑了——周六下午放假，周日也放一天。\n\n换个节奏：以后每天花一点时间写博客，把系列写长一点，也写得更好一点。",
            "url": "https://kyriechao.github.io/posts/2026/07/25-diary",
            "title": "日志",
            "summary": "Rust 练习、新建博客系列，以及培训一个月以来的感受",
            "date_modified": "2026-07-25T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/25-rust-day1",
            "content_html": "\n# Rust 入门（Day 1）\n\n## 一、Rust 概述\n\nRust 是一门专注于**安全、并发和性能**的现代系统编程语言。它由 Mozilla 员工 Graydon Hoare 于 2006 年作为个人项目启动，2015 年 5 月发布 1.0 稳定版，目前由 Rust 基金会管理。\n\n### 核心特性\n\n| 特性              | 说明                                                         |\n|-----------------| ------------------------------------------------------------ |\n| **内存安全**        | 通过所有权（Ownership）、借用（Borrowing）、生命周期（Lifetime）机制，在编译期消除悬垂指针、双重释放、数据竞争等问题，无需垃圾回收器（GC） |\n| **零成本抽象**       | 高级语法糖（迭代器、闭包、泛型等）编译后性能等同于手写底层代码 |\n| **并发安全**        | `Send` 和 `Sync` trait 在编译期检查并发安全性，避免数据竞争  |\n| **模式匹配**        | `match` 表达式和 `if let` 等模式匹配机制，强制处理所有可能情况 |\n| **强类型系统**       | 代数数据类型（enum 可携带数据）、Trait（类似接口但更强）、泛型等 |\n| **无需运行时**       | 无 GC、无解释器，运行时极小，可嵌入 C 程序，也可用于裸机开发 |\n\n### 适用场景\n\n- 系统编程（操作系统、驱动、嵌入式）\n- WebAssembly 前端\n- 网络服务、CLI 工具\n- 区块链、数据库等对性能/安全性要求高的领域\n\n---\n\n## 二、环境安装\n\n### 2.1 核心工具链三件套\n\n| 工具       | 作用                                                         |\n| ---------- | ------------------------------------------------------------ |\n| **rustup** | Rust 工具链管理器，用于安装、更新、切换 Rust 版本（stable / beta / nightly） |\n| **rustc**  | Rust 编译器，将 `.rs` 源码编译为可执行文件                   |\n| **cargo**  | Rust 的包管理器和构建系统，负责依赖管理、编译、测试、发布等  |\n\n### 2.2 Linux / macOS 安装\n\n```shell\ncurl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh\n```\n\n默认安装即可，rustup 会自动配置 PATH。\n\n### 2.3 Windows 安装\n\n访问 https://rust-lang.org/tools/install/ 下载安装包。\n\nWindows 下可能需要 Visual Studio Build Tools（MSVC 工具链）。如果不希望安装 VS，可自定义安装选择 MinGW 工具链（略繁琐）。\n\n### 2.4 验证安装\n\n```shell\nrustc --version\n```\n\n```shell\ncargo --version\n```\n\n### 2.5 更新与卸载\n\n```shell\n# 更新 Rust 工具链到最新稳定版\nrustup update\n\n# 卸载整个 Rust 工具链\nrustup self uninstall\n```\n\n> 后续示例以 Linux 环境为准。\n\n---\n\n## 三、第一个程序：Hello World\n\n### 3.1 手动创建\n\n```shell\nmkdir hello_world\ncd hello_world\n```\n\n新建 `main.rs`：\n\n```rust\nfn main() {\n    println!(\"Hello World\");\n}\n```\n\n**代码解析：**\n\n- `fn main()` — 程序的入口函数。每个 Rust 可执行程序必须有 `main` 函数。\n- `println!` — 这是一个**宏（Macro）**，不是普通函数。宏名后带 `!` 是 Rust 的命名约定。`println!` 会将文本输出到标准输出并自动换行。\n- `\"Hello World\"` — 字符串字面量，类型是 `&str`（字符串切片，后面会详细讲）。\n- 每行语句以 `;` 结尾。\n\n### 3.2 编译运行\n\n```shell\nrustc main.rs\n./main\n```\n\n输出：\n\n```\nHello World\n```\n\n`rustc` 直接编译单个文件，适合简单场景。正式项目使用 Cargo 管理。\n\n---\n\n## 四、Hello Cargo\n\n### 4.1 创建 Cargo 项目\n\n```shell\ncargo new hello_cargo\ncd hello_cargo\n```\n\n生成的文件结构：\n\n```\nhello_cargo/\n├── src/\n│   └── main.rs          # 程序入口\n├── Cargo.toml            # 项目元数据和依赖配置\n└── .gitignore            # Git 忽略规则（自动生成）\n```\n\n### 4.2 Cargo.toml 结构\n\n```toml\n[package]\nname = \"hello_cargo\"\nversion = \"0.1.0\"\nedition = \"2024\"\n\n[dependencies]\n```\n\n| 字段             | 说明                                                         |\n| ---------------- | ------------------------------------------------------------ |\n| `[package]`      | 包元数据段                                                   |\n| `name`           | 包名（创建时指定）                                           |\n| `version`        | 版本号，遵循语义化版本（SemVer）                             |\n| `edition`        | Rust 版本代号（2015 / 2018 / 2021 / 2024），影响语言某些特性的默认行为 |\n| `[dependencies]` | 项目依赖列表，每行一个 crate 名称和版本约束                  |\n\n### 4.3 Cargo 常用命令\n\n```shell\n# 编译项目（debug 模式，未优化，包含调试信息）\ncargo build\n\n# 编译并运行\ncargo run\n\n# 仅检查代码能否通过编译（不生成可执行文件，速度更快）\ncargo check\n\n# 编译为 release 模式（优化过，适合发布）\ncargo build --release\n```\n\n| 命令                    | 生成可执行文件 | 速度 | 适用场景           |\n| ----------------------- | :------------: | :--: | ------------------ |\n| `cargo build`           |       是       |  慢  | 需要运行调试       |\n| `cargo run`             |       是       |  慢  | 编译后立即运行     |\n| `cargo check`           |       否       |  快  | 开发中反复检查语法 |\n| `cargo build --release` |       是       | 最慢 | 正式发布           |\n\n编译产物位于 `target/` 目录：\n\n- debug 模式：`target/debug/`\n- release 模式：`target/release/`\n\n### 4.4 Cargo.lock\n\n首次 `cargo build` 会生成 `Cargo.lock`，记录依赖的**精确版本号**（包括间接依赖），确保同一项目在不同机器上编译结果一致。\n\n- 你永远不需要手动编辑这个文件，Cargo 会自动管理。\n- 对于可执行程序，建议将 `Cargo.lock` 纳入版本控制（Git）；对于库项目则无需提交。\n\n---\n\n## 五、猜数字游戏\n\n本节通过一个完整示例，逐步引入 Rust 的核心概念。\n\n### 5.1 处理用户输入\n\n```rust\nuse std::io;                          // 将 io 模块引入当前作用域\n\nfn main() {\n    println!(\"Guess the number!\");\n    println!(\"Please input your guess.\");\n\n    let mut guess = String::new();    // 创建可变 String 变量，初始为空\n\n    io::stdin()                        // 获取标准输入句柄\n        .read_line(&mut guess)         // 读取一行到 guess，&mut 表示可变引用\n        .expect(\"Failed to read line\"); // 如果读取失败则崩溃并打印此信息\n\n    println!(\"You guessed: {guess}\");  // {} 是占位符，会替换为变量值\n}\n```\n\n**知识点梳理：**\n\n#### `use` 语句\n\n将模块/类型引入当前作用域，避免写完整路径。类似于 Python 的 `from ... import`。\n\n```rust\nuse std::io;            // 现在可以直接写 io::stdin()\n// 如果不写 use，则需要写 std::io::stdin()\n```\n\n本次用到 `std::io` 标准库中的输入/输出功能。\n\n#### `let` 与可变性（Mutability）\n\n```rust\nlet x = 5;          // 不可变绑定（默认）\nlet mut y = 5;      // 可变绑定，允许修改\n```\n\nRust 中变量**默认不可变**。这是安全性设计的重要一环——你不需要担心某个值在你不注意时被修改。需要修改时必须显式声明 `mut`。\n\n#### `String::new()` — 关联函数\n\n`String` 是标准库提供的**可增长、UTF-8 编码**的字符串类型。`::new` 中的 `new` 是 `String` 类型的**关联函数（Associated Function）**——类似于其他语言中的\"静态方法\"，它不作用于某个实例，而是作用于类型本身。`new` 是 Rust 中创建新实例的惯用命名，这里用于创建一个空字符串。\n\n#### `&mut guess` — 可变引用\n\n```rust\nread_line(&mut guess)\n```\n\n- `&` 表示**引用（Reference）**，允许函数访问数据而不获取所有权（Ownership）。\n- `&mut` 表示**可变引用**，被引用方可以修改所指向的数据。\n- `read_line` 需要可变引用，因为它要向 `guess` 中追加用户输入的内容。\n\n> 所有权和引用是 Rust 最核心的安全机制，后续课程会详细展开。\n\n#### `Result` 枚举与 `expect`\n\n```rust\nio::stdin().read_line(&mut guess).expect(\"Failed to read line\");\n```\n\n`read_line` 的返回类型是 `Result<usize, Error>`，这是一个**枚举（Enum）**，有两个变体：\n\n```rust\nenum Result<T, E> {\n    Ok(T),    // 成功时包含返回值（这里是读取的字节数）\n    Err(E),   // 失败时包含错误信息\n}\n```\n\n`expect` 方法：\n\n- 如果 `Result` 是 `Ok`，返回其中的值。\n- 如果 `Result` 是 `Err`，程序**崩溃**并打印你传入的错误信息。\n\n> 实际项目中更常用 `?` 运算符或 `match` 来优雅地处理错误，而不是直接 `expect`。后续课程会讲。\n\n#### `println!` 占位符\n\n```rust\nprintln!(\"You guessed: {guess}\");\n```\n\n`{}` 是占位符（与 C 的 `printf` 不同，Rust 无需指定 `%d`、`%s` 等格式），会自动根据类型格式化。多个值用逗号分隔：\n\n```rust\nprintln!(\"x = {}, y = {}\", x, y);\n```\n\n---\n\n### 5.2 生成秘密数字\n\nRust 标准库不包含随机数功能，需要引入第三方 crate：`rand`。\n\n**步骤 1：添加依赖**\n\n编辑 `Cargo.toml`：\n\n```toml\n[dependencies]\nrand = \"0.8.5\"\n```\n\n`\"0.8.5\"` 是语义化版本约束，表示 `>= 0.8.5 且 < 0.9.0` 的版本均兼容。你也可以写 `\"0.8\"` 或使用更精确的约束如 `\"^0.8.5\"`。\n\n当你下次执行 `cargo build` 时，Cargo 会自动：\n\n1. 从 [crates.io](https://crates.io) 下载 `rand` 及其所有依赖。\n2. 编译所有依赖。\n3. 更新 `Cargo.lock` 锁定精确版本。\n\n**步骤 2：使用 rand**\n\n```rust\nuse std::io;\nuse rand::Rng;                        // 引入 Rng trait（包含 gen_range 等方法）\n\nfn main() {\n    println!(\"Guess the number!\");\n\n    let secret_number = rand::thread_rng().gen_range(1..=100);\n    // thread_rng()：获取当前线程的随机数生成器\n    // gen_range(1..=100)：生成 1 到 100（闭区间，含100）之间的随机数\n\n    println!(\"The secret number is: {secret_number}\");\n\n    println!(\"Please input your guess.\");\n\n    let mut guess = String::new();\n\n    io::stdin()\n        .read_line(&mut guess)\n        .expect(\"Failed to read line\");\n\n    println!(\"You guessed: {guess}\");\n}\n```\n\n**知识点：**\n\n- `rand::thread_rng()` — 返回当前线程的**线程局部**随机数生成器，使用 CSPRNG（密码安全伪随机数生成器）。\n- `gen_range(1..=100)` — `1..=100` 是**闭区间**范围（含 100），对应的半开区间写法是 `1..101`。\n- `Rng` 是一个 **trait**（类似其他语言中的接口），`gen_range` 方法定义在 `Rng` trait 中，必须先 `use rand::Rng` 才能调用。\n\n> Cargo.lock 确保任何人拉取你的项目后运行 `cargo build` 都会得到完全相同的依赖版本，从 `Cargo.toml` 的宽松版本号回到 `Cargo.lock` 的精确版本号。这个机制保证了可重现构建（Reproducible Build）。\n\n---\n\n### 5.3 比较数字\n\n```rust\nuse std::cmp::Ordering;               // 引入 Ordering 枚举\nuse std::io;\nuse rand::Rng;\n\nfn main() {\n    println!(\"Guess the number!\");\n\n    let secret_number = rand::thread_rng().gen_range(1..=100);\n\n    println!(\"The secret number is: {secret_number}\");\n    println!(\"Please input your guess.\");\n\n    let mut guess = String::new();\n\n    io::stdin()\n        .read_line(&mut guess)\n        .expect(\"Failed to read line\");\n\n    let guess: u32 = guess            // shadowing：新建同名变量，类型从 String 转为 u32\n        .trim()                        // 去除首尾空白字符（包括换行符 \\n）\n        .parse()                       // 将字符串解析为指定类型（由 : u32 类型注解决定）\n        .expect(\"Please type a number!\");\n\n    println!(\"You guessed: {guess}\");\n\n    match guess.cmp(&secret_number) {  // cmp 返回 Ordering 枚举值\n        Ordering::Less => println!(\"Too small!\"),\n        Ordering::Greater => println!(\"Too big!\"),\n        Ordering::Equal => println!(\"You win!\"),\n    }\n}\n```\n\n#### 你初次尝试编译会遇到的类型错误\n\n```\nerror[E0308]: mismatched types\n  expected reference `&String`\n     found reference `&{integer}`\n```\n\n`guess`（String）和 `secret_number`（i32 / u32 等整数类型）类型不同，Rust 不允许直接比较。所以我们需要做类型转换。\n\n#### 遮蔽（Shadowing）\n\n```rust\nlet mut guess = String::new();    // 第一次声明：String 类型\n// ... 读取输入 ...\nlet guess: u32 = guess            // 第二次声明：u32 类型，遮蔽了前面的 guess\n    .trim()\n    .parse()\n    .expect(\"Please type a number!\");\n```\n\n遮蔽让你可以**重用变量名**，同时改变其类型和可变性。这与 `mut` 不同：\n\n| 特性           | `mut`    | 遮蔽                             |\n| -------------- | -------- | -------------------------------- |\n| 能否改变类型   | 否       | 是                               |\n| 赋值后是否可变 | 是       | 取决于新声明的 `let` / `let mut` |\n| 旧值           | 直接修改 | 旧值被\"隐藏\"，底层数据可能仍存在 |\n\n> 遮蔽是 Rust 的惯用写法，尤其适合类型转换场景——与其发明 `guess_str` 这样的变量名，不如直接用 `let` 覆盖。\n\n#### `trim()` 与 `parse()`\n\n- `trim()` — 返回 `&str`，去除字符串首尾的空白字符（空格、制表符、换行符等）。这里用来去掉用户按回车时产生的 `\\n`。\n- `parse()` — 返回 `Result<T, ParseIntError>`。目标类型 `T` 由左侧的**类型注解** `: u32` 决定（Rust 的类型推断是双向的）。\n\n#### `match` 表达式\n\n```rust\nmatch guess.cmp(&secret_number) {\n    Ordering::Less => println!(\"Too small!\"),\n    Ordering::Greater => println!(\"Too big!\"),\n    Ordering::Equal => println!(\"You win!\"),\n}\n```\n\n`match` 由**分支（Arm）** 组成，每个分支：`模式 => 表达式`。\n\n**关键特性：**\n\n- **穷尽性（Exhaustiveness）**：必须覆盖所有可能的分支，否则编译报错。这里 `Ordering` 恰好有 `Less`、`Greater`、`Equal` 三个变体，全部处理了。\n- `cmp` 是值的比较方法，返回 `Ordering` 枚举：\n\n```rust\nenum Ordering {\n    Less,\n    Equal,\n    Greater,\n}\n```\n\n---\n\n### 5.4 循环与退出\n\n```rust\nuse std::cmp::Ordering;\nuse std::io;\nuse rand::Rng;\n\nfn main() {\n    println!(\"Guess the number!\");\n\n    let secret_number = rand::thread_rng().gen_range(1..=100);\n\n    println!(\"The secret number is: {secret_number}\");\n\n    loop {                               // 无限循环\n        println!(\"Please input your guess.\");\n\n        let mut guess = String::new();\n\n        io::stdin()\n            .read_line(&mut guess)\n            .expect(\"Failed to read line\");\n\n        let guess: u32 = guess.trim().parse().expect(\"Please type a number!\");\n\n        println!(\"You guessed: {guess}\");\n\n        match guess.cmp(&secret_number) {\n            Ordering::Less => println!(\"Too small!\"),\n            Ordering::Greater => println!(\"Too big!\"),\n            Ordering::Equal => {\n                println!(\"You win!\");\n                break;                    // 猜对后跳出循环\n            }\n        }\n    }\n}\n```\n\n**`loop` 循环：**\n\n- `loop` 创建无限循环，等价于 `while true`，但 Rust 编译器知道 `loop` 至少会执行一次。\n- 可使用 `break` 退出循环，`continue` 跳到下一次迭代。\n- `loop` 可以带返回值（将值放在 `break` 后面），这是 Rust 独有的特性：\n\n```rust\nlet result = loop {\n    // ...\n    break 42;         // loop 的返回值\n};\n```\n\n---\n\n### 5.5 处理无效输入\n\n当前的程序，如果用户输入非数字（如 \"abc\"），`parse().expect()` 会导致程序崩溃。我们希望忽略无效输入，让用户继续猜：\n\n```rust\n// 将原来的\nlet guess: u32 = guess.trim().parse().expect(\"Please type a number!\");\n\n// 改为 match 处理 Result\nlet guess: u32 = match guess.trim().parse() {\n    Ok(num) => num,      // 解析成功，将数值绑定到 num，作为 match 表达式的值\n    Err(_) => continue,  // 解析失败，忽略本次输入，进入下一次循环\n};\n```\n\n**知识点：**\n\n- 这里用 `match` 优雅地处理了 `Result` 的两种可能，替代了粗暴的 `expect` 崩溃。\n- `Err(_)` 中的 `_` 是**通配符**，表示\"匹配任意错误值，但我不关心它的内容\"。\n- `continue` 跳回循环开头，让用户重新输入。\n\n---\n\n### 5.6 完整代码\n\n```rust\nuse std::cmp::Ordering;\nuse std::io;\nuse rand::Rng;\n\nfn main() {\n    println!(\"Guess the number!\");\n\n    let secret_number = rand::thread_rng().gen_range(1..=100);\n\n    loop {\n        println!(\"Please input your guess.\");\n\n        let mut guess = String::new();\n\n        io::stdin()\n            .read_line(&mut guess)\n            .expect(\"Failed to read line\");\n\n        let guess: u32 = match guess.trim().parse() {\n            Ok(num) => num,\n            Err(_) => continue,\n        };\n\n        println!(\"You guessed: {guess}\");\n\n        match guess.cmp(&secret_number) {\n            Ordering::Less => println!(\"Too small!\"),\n            Ordering::Greater => println!(\"Too big!\"),\n            Ordering::Equal => {\n                println!(\"You win!\");\n                break;\n            }\n        }\n    }\n}\n```\n\n（正式发布时可将打印 `secret_number` 那行删除，否则游戏就没有意义了。）\n\n---\n\n## 六、本节知识点汇总\n\n| 概念                          | 简要说明                                  |\n| ----------------------------- | ----------------------------------------- |\n| `rustup` / `rustc` / `cargo`  | 工具链管理器 / 编译器 / 包管理器+构建系统 |\n| `fn main()`                   | 程序入口                                  |\n| `println!`                    | 宏（带 `!`），格式化输出并换行            |\n| `let` / `let mut`             | 变量绑定，默认不可变                      |\n| `use`                         | 将模块/类型引入作用域                     |\n| `String` / `&str`             | 可增长字符串 / 字符串切片                 |\n| `String::new()`               | 关联函数（静态方法），创建空字符串        |\n| `&mut guess`                  | 可变引用，被引用方可以修改数据            |\n| `Result<T, E>`                | 枚举：`Ok(T)` 成功 / `Err(E)` 失败        |\n| `expect(msg)`                 | `Result` 的方法，成功取值、失败崩溃       |\n| `match`                       | 模式匹配，穷尽性检查                      |\n| 遮蔽（Shadowing）             | 用 `let` 重新声明同名变量，可改变类型     |\n| `trim()`                      | 去除首尾空白                              |\n| `parse()`                     | 字符串解析为目标类型，返回 `Result`       |\n| `loop` / `break` / `continue` | 无限循环 / 退出 / 下一次迭代              |\n| `Cargo.toml` / `Cargo.lock`   | 项目配置 / 依赖精确版本锁定               |\n| `edition`                     | Rust 语言版本代号（2015/2018/2021/2024）  |\n| Crate                         | Rust 的包单元（库/可执行文件）            |\n| Trait                         | 定义共享行为的接口，如 `Rng`              |\n\n---\n\n## 七、课后练习\n\n1. 修改猜数字游戏的数字范围为 `1..=1000`，观察程序行为变化。\n2. 试着把 `println!(\"You guessed: {guess}\");` 中的 `{guess}` 改为 `{}`，将 `guess` 作为第二个参数传入，体会位置参数写法。\n3. 在循环中加入一个计数器，记录用户猜了几次才猜中，并在猜中后打印出来。\n4. 尝试用 `cargo build --release` 编译项目，比较 `target/debug/` 和 `target/release/` 中可执行文件的大小差异。\n5. （选做）查看 `cargo doc --open` 命令的效果——它会为你的项目及其依赖生成 HTML 文档并在浏览器中打开。",
            "url": "https://kyriechao.github.io/posts/2026/07/25-rust-day1",
            "title": "Rust 学习计划：3 个月从语法入门到 CLI 实战",
            "summary": "制定一份清晰、可执行的 Rust 学习路线，涵盖基础语法、所有权机制、异步编程及实际项目练手，目标是在 3 个月内产出至少两个可用的命令行工具。",
            "date_modified": "2026-07-25T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/24-phase1",
            "content_html": "\n# 阶段一：培训 🏗️\n\n> **时间范围**：2026 年 7 月 — 2027 年 6 月（12 个月）\n> **两段式**：上半年（7-12 月）完成机构培训，下半年（1-6 月）实习 + 毕业。\n\n---\n\n## 当前起点\n\n- 在北京昌平，机构培训刚开始\n- 培训机构有四阶段路线：Linux C → 嵌入式应用 → MCU → Linux 驱动\n- 2026 年 12 月底培训结束，2027 年 6 月学校毕业\n- 长期愿景见 [总纲](./24-plan)\n\n---\n\n## 上半场：培训期（2026.07 — 2026.12，6 个月）\n\n> 目标是**跟上每一节课，每一行代码都自己敲一遍**。6 个月很紧，不存在\"后面再补\"。\n\n### 1. Linux C 语言与数据结构（约 7-8 月）\n\n| 模块 | 内容 | 自检 |\n|------|------|------|\n| Linux 基本命令 | 文件操作、用户管理、网络管理、权限 | 不用查资料能完成日常操作 |\n| C 语言基础 | 变量、运算符、控制语句、函数 | 徒手写无语法错误 |\n| C 语言核心 | 数组、指针、结构体、联合体 | 能解释\"指针的指针\"和应用场景 |\n| 数据结构 | 顺序表、链表、栈、队列、树 | 能手写每种结构的增删改查 |\n\n**检验标准**：给你一个 C 编程题（如实现字符串库函数、链表逆序），不查资料能写对。\n\n### 2. 嵌入式应用开发（约 9-10 月）\n\n| 模块 | 内容 | 自检 |\n|------|------|------|\n| UNIX 环境编程 | 文件 IO、进程、线程、信号、IPC、网络编程 | 能写一个并发网络服务器 |\n| 阶段项目 | UDP 多媒体组播系统 | **这是简历上第一个项目，认真做** |\n| C++ | 封装、继承、多态、智能指针、STL | 能读懂 C++ 项目代码 |\n| QT | 信号与槽、常用组件 | 能搭一个带 UI 的小工具 |\n\n**检验标准**：组播项目能跑通，且能给别人讲清楚架构和数据流。\n\n### 3. MCU 开发：裸机与 RTOS（约 11 月）\n\n| 模块 | 内容 | 自检 |\n|------|------|------|\n| ARM 体系结构 | 寄存器、中断向量、时钟树 | 能对着芯片手册查寄存器 |\n| 外设驱动 | GPIO、LED、USART、I2C、SPI | 手写驱动，不靠 HAL 生成 |\n| 项目 | 物联网数据采集器 | **简历上第二个项目** |\n\n**检验标准**：传感器数据能采集到并通过串口打印出来。\n\n### 4. Linux 内核驱动开发（约 12 月）\n\n| 模块 | 内容 | 自检 |\n|------|------|------|\n| 构建嵌入式 Linux | Uboot、Kernel、BuildRoot | 能自己编译出一个能跑的系统 |\n| 内核编程 | 模块、调试、链表、文件操作 | 能写一个简单的内核模块 |\n| 设备驱动 | 字符设备驱动框架 | 能写一个 LED 的字符驱动 |\n| 面试突击 | 常见面试题、项目梳理 | —— |\n\n**检验标准**：能讲清楚\"用户态程序怎么调用到驱动\"这条链路。\n\n### 培训期总产出\n\n- [ ] UDP 多媒体组播系统（可写进简历）\n- [ ] 物联网数据采集器（可写进简历）\n- [ ] 一个简单的 Linux 字符驱动\n- [ ] 每天一行不落地敲完所有课堂代码\n\n---\n\n## 下半场：实习期（2027.01 — 2027.06，6 个月）\n\n> 培训结束后的核心任务：**找到一份嵌入式实习，积累真实的工程经验**。\n\n### 实习目标\n\n| 优先级 | 目标 | 说明 |\n|------|------|------|\n| P0 | 找到一份嵌入式相关实习 | 优先单片机/驱动方向，QT 上位机也可作为保底 |\n| P1 | 实习期间至少参与一个完整的产品/项目开发 | 哪怕只负责一个小模块，完整跟下来 |\n| P2 | 确认自己更偏好\"应用层\"还是\"底层\" | 这个几乎只能靠实习来验证 |\n\n### 找实习时间线\n\n| 时间 | 动作 |\n|------|------|\n| 12 月下旬 | 整理简历，两个培训项目写清楚，附 GitHub 链接 |\n| 1 月初 | 开始投递，同学/老师/机构的内推渠道优先 |\n| 1-2 月 | 面试 + 入职 |\n| 3-6 月 | 实习 + 毕业设计/论文 |\n\n### 简历项目包装要点\n\n1. **UDP 多媒体组播系统**：强调 socket 编程、并发模型、协议设计\n2. **物联网数据采集器**：强调外设驱动、传感器通信、数据上报逻辑\n3. 每个项目用\"解决了什么问题 → 用了什么技术 → 达到了什么效果\"的三段式描述\n\n---\n\n## 技术扩展（全年）\n\n> 培训期课程已经很满，扩展只跟 1 个。\n\n| 方向 | 深度 | 方式 |\n|------|------|------|\n| 智能硬件 | 了解 | 碎片时间看开源硬件项目（ESP32、树莓派），理解设计思路 |\n\nAI 工程化、机器人、云端系统 **本阶段不碰**。\n\n---\n\n## 长期创造 — Kria 语言\n\n> 培训期不动，放到实习稳定之后（3-4 月再用空闲时间启动）。\n\n**目标不变**：产出一份 A4 篇幅的设计动机文档，回答三个问题：\n\n1. Kria 解决什么问题？\n2. 现有语言哪里不够用？\n3. 如果只做一个差异特性，是什么？\n\n---\n\n## 认知建设\n\n> 培训期节奏紧，目标压低，只读 2 本（半年一本）。\n\n| 领域 | 任务 | 最低标准 |\n|------|------|----------|\n| 技术之外 | 读 1 本自己最感兴趣的领域（历史/经济/哲学/心理 任选） | 能复述核心观点 |\n| 职业相关 | 读 1 本软件工程或嵌入式行业的经典书（如《程序员修炼之道》或《嵌入式系统设计》） | 有 5 条可用到实习中的收获 |\n\n---\n\n## 财富建设\n\n| 项目 | 内容 |\n|------|------|\n| **记账** | 坚持到 12 月底，看培训期间总共花了多少钱 |\n| **储蓄** | 实习工资的第一件事：存 20%，剩下的当生活费 |\n| **学习** | 有空读 1 本理财入门书，没空先放一放 |\n\n---\n\n## 身体系统\n\n> 培训期每天坐着上课，身体更容易出问题，这两个反而要守住。\n\n| 习惯 | 标准 | 验收 |\n|------|------|------|\n| 固定睡眠 | 23:30 前躺下，7:00 前起 | 连续 28 天达标 |\n| 每周运动 | 每次 ≥20 分钟，每周 ≥2 次 | 强度降低，频率守住 |\n\n**培训期运动降级到每周 2 次、每次 20 分钟**——时间太紧，保持住\"动\"的习惯就行，别断。\n\n---\n\n## 社会连接\n\n| 阶段 | 动作 |\n|------|------|\n| 培训期 | 加同学的微信、进技术群；GitHub 上把课堂练习整理成仓库 |\n| 实习期 | 第一篇文章可以写\"从培训到实习：嵌入式入门的真实感受\"；主动请同事吃饭请教问题 |\n\n---\n\n## 记录系统\n\n| 形式 | 频率 | 最低标准 |\n|------|------|----------|\n| 日志 | 每天 | 今天学了什么 / 遇到什么坑 / 明天学什么（三行即可） |\n| 复盘 | 每月 | 对着本文档逐项打分，偏差在哪里 |\n| 学期总结 | 12 月底 | 培训期回顾 + 简历整理 |\n| 年度总结 | 6 月底 | 阶段一整体回顾，制定阶段二 |\n\n---\n\n## 里程碑节点\n\n| 时间 | 节点 | 内容 |\n|------|------|------|\n| 2026.08 | M1 · 上轨 | 日志习惯连续 30 天，C 语言 + 数据结构收尾 |\n| 2026.10 | M2 · 深入 | UNIX 编程学完，组播项目完成，系统编程能力初具 |\n| 2026.12 | M3 · 培训结业 | MCU + 驱动学完，简历整理完毕，开始投递 |\n| 2027.03 | M4 · 实习中 | 实习在岗，Kria 设计动机动笔，写第一篇技术文章 |\n| 2027.06 | M5 · 毕业 | 实习/试用期结束，毕业，阶段一总结，制定阶段二 |\n\n---\n\n## 不做清单（阶段一特供版）\n\n- ❌ 培训期不自学课外技术——课上内容消化完已经是 100% 负荷\n- ❌ 不跟同学比进度——按自己的节奏来，培训的目标是\"学会了\"不是\"学完了\"\n- ❌ 实习不挑活——哪怕是写文档、调 UI 也是工程经验\n- ❌ Kria 培训期不碰\n- ❌ 不借钱投资 / 不碰杠杆\n\n---\n\n*下次审视：2026 年 8 月底（M1 节点）*\n",
            "url": "https://kyriechao.github.io/posts/2026/07/24-phase1",
            "title": "阶段一：培训",
            "summary": "2026.07 — 2027.06 完成机构培训，找到嵌入式实习，顺利毕业",
            "date_modified": "2026-07-24T02:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/07/24-plan",
            "content_html": "\n# 陈鸽涛的个人计划 🚀\n\n> **本文定位**：5-10 年大方向总纲。具体执行计划见阶段性子文档（阶段一、阶段二……）。\n\n**现状**：北京昌平，学习嵌入式方向。\n\n---\n\n## 1. 职业主线\n\n| 项目 | 内容 |\n|------|------|\n| **方向** | 嵌入式系统工程师 |\n| **目标** | 软硬件结合的系统能力 |\n\n---\n\n## 2. 技术扩展\n\n以嵌入式为核心，向外延伸：\n\n| 方向 | 说明 |\n|------|------|\n| 智能硬件 | 传感器、边缘计算、低功耗设计 |\n| AI 工程化 | 模型部署、端侧推理、嵌入式 AI |\n| 机器人 | 运动控制、ROS、机电一体化 |\n| 云端系统 | 设备管理、数据通道、云边协同 |\n\n---\n\n## 3. 长期创造\n\n### 3.1 Kria 语言\n\n> 探索程序设计思想与系统设计思想，在软硬件结合场景中验证。\n\n| 阶段 | 内容 |\n|------|------|\n| 1. 设计 | 完善语言设计文档，明确要解决的问题 |\n| 2. 实现 | 解释器 / 编译器原型 |\n| 3. 融合 | 探索软硬件结合场景 |\n| 4. 应用 | 在实际项目中试用并迭代 |\n\n### 3.2 个人技术品牌\n\n- 技术博客 / 笔记输出\n- 开源项目维护\n- 社区分享与演讲\n\n---\n\n## 4. 认知建设\n\n持续阅读，建立跨领域认知框架：\n\n| 领域 | 目的 |\n|------|------|\n| 历史 | 理解社会变化规律 |\n| 经济 | 理解资源流动逻辑 |\n| 哲学 | 建立个人价值体系 |\n| 心理学 | 理解人类行为模式 |\n\n---\n\n## 5. 财富建设\n\n**目标**：建立长期资产，通过能力与认知积累财富。\n\n**学习路径**：\n- 个人财务管理\n- 投资基础\n- 企业分析\n- 宏观经济\n\n**原则**：不追求赌博式收益，靠能力和认知积累。\n\n---\n\n## 6. 身体系统\n\n**目标**：保持长期高性能身体状态。\n\n**习惯**：\n- 固定睡眠时间\n- 每周规律运动\n- 控制体脂率\n- 定期体检\n\n---\n\n## 7. 社会连接\n\n- 开源项目参与\n- 技术社区建设\n- 同行交流合作\n\n---\n\n## 8. 记录系统\n\n| 形式 | 频率 | 目的 |\n|------|------|------|\n| 日志 | 每日 | 捕捉当天思考与进展 |\n| 复盘 | 每月 | 回顾得失，调整方向 |\n| 年度总结 | 每年 | 对照计划，校准长期目标 |\n\n---\n\n## 不做清单\n\n- ❌ 不追逐短期热点\n- ❌ 不同时深入超过 3 个技术方向\n- ❌ 不为了焦虑而学习\n- ❌ 不用理论替代实践\n\n## 阶段文档\n\n- [阶段一：培训](./24-phase1.md)\n\n## 下一步：阶段划分（预告）\n\n> 后续将创建阶段性子文档，每个阶段包含：\n> - 时间范围（如 2026.07 — 2027.07）\n> - 阶段重点（从上方 8 个板块中选出当期聚焦项）\n> - 可衡量指标\n> - 阶段复盘节点\n\n*大方向每年审视一次，阶段计划每季度微调。*\n\n\n",
            "url": "https://kyriechao.github.io/posts/2026/07/24-plan",
            "title": "个人计划",
            "summary": "陈鸽涛的未来计划",
            "date_modified": "2026-07-24T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/2/26-operations-and-appendix",
            "content_html": "\n# 🚀 Failure 框架使用教程 - 4. 生产运维与附录\n\n这一篇关注“上线以后你最关心的事”：\n- 监控：失败率、耗时、QPS 📈\n- 排障：TraceId、日志一键定位 🔎\n- 国际化：文案缓存与刷新 🌍\n\n---\n\n## 1. 📊 可观测性（Micrometer）\n\n如果你接入了 `failure-observability-spring-boot-starter`（且项目里有 `MeterRegistry`），框架会自动上报校验相关指标。\n\n建议在 Grafana 上至少做 3 张图：\n- 校验耗时（Timer）\n- 校验次数（Counter）\n- 校验失败率（fail / total）\n\n---\n\n## 2. 🧵 TraceId（默认关闭，按需开启）\n\n### 2.1 开启后的收益\n开启 `trace-id` 后，你会获得三件事：\n- 响应体（可选）/响应头（可选）回传 TraceId，前端报错时能把它带回来\n- MDC 注入：日志自动包含 TraceId\n- 控制台日志里可以直接全文搜索 TraceId，定位整条请求链路\n\n### 2.2 推荐配置（开启）\n\n```yaml\nfail-fast:\n  trace-id:\n    enabled: true\n    header-name: X-Trace-Id\n    response-header: true\n    response-header-name: X-Trace-Id\n    generate-if-missing: true\n    mdc-enabled: true\n    mdc-key: traceId\n```\n\n### 2.3 关闭语义（方案 A）\n\n```yaml\nfail-fast:\n  trace-id:\n    enabled: false\n```\n\n当 `enabled=false` 时：\n- TraceId Filter 不会注册\n- 异常响应体将不再返回 `traceId` 字段（关掉就是彻底没有）\n\n---\n\n## 3. 🧾 日志 Pattern（把 TraceId 打到日志里）\n\n如果你启用了 `mdc-enabled=true`，建议把 MDC 字段拼到 console pattern：\n\n```yaml\nlogging:\n  pattern:\n    console: \"%clr(%d{${LOG_DATEFORMAT_PATTERN:yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:%5p}) %clr(${PID:-}){magenta} %clr([${spring.application.name:-}]){faint} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%replace([%X{traceId:-}]){'^\\\\[\\\\]$',''}%n${LOG_EXCEPTION_CONVERSION_WORD:%wEx}\"\n```\n\n这样前端拿到的 `traceId` 可以直接在日志里一搜就定位到错误原因。\n\n---\n\n## 4. 🌍 I18n 缓存调优\n\nFailure 的错误信息可以走国际化资源文件。缓存配置项是：\n\n```yaml\nfail-fast:\n  i18n:\n    cache-seconds: 3600\n```\n\n建议：\n- 开发环境：`cache-seconds: 0`（方便你改文案立即生效）\n- 生产环境：保持默认（减少磁盘 IO）\n\n---\n\n## 5. 📚 附录：注解速查表\n\n| 注解 | 位置 | 作用 |\n|---|---|---|\n| `@Validate` | 方法 | 声明式校验总开关（scene/groups/fast） |\n| `@Scene` | 字段 | 场景化过滤 JSR-303 violation |\n| `@SkipValidation` | 参数 | 指定参数不参与校验 |\n| `@FailFastBody` | 参数 | 可选 RequestBody（required=false） |\n\n---\n\n## 6. 🧩 FAQ（常见问题）\n\n### 6.1 为什么我关闭 trace-id 仍看到 traceId？\n如果你把 `fail-fast.trace-id.enabled=false` 后仍看到 `traceId`，通常是：\n- 你没生效到当前环境配置文件（profile 配置覆盖）\n- 你启用了旧版本（升级后需要重新发布）\n\n### 6.2 为什么日志里能点击 `(SomeClass.java:34)`？\n框架在异常日志里打印了触发异常的源码位置，IDEA 会识别并提供点击跳转，这对排查定位非常省时间。\n",
            "url": "https://kyriechao.github.io/posts/2026/03/2/26-operations-and-appendix",
            "title": "Failure 框架指南",
            "summary": "Spring Boot 参数校验新选择：无缝兼容 JSR-303，支持 Fail-Fast 快速失败与 Fail-Strict 全量收集，提供 Scene+Group 双维度场景校验与统一异常响应。",
            "date_modified": "2026-03-26T04:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/2/26-functional-result",
            "content_html": "\n# 🚀 Failure 框架使用教程 - 3. 函数式结果处理 (Result)\n\n如果你讨厌 `try-catch` 满天飞，Failure 提供了另一套哲学：**Monad Result**。\n\n## 1. 🤔 Result vs 异常的选择决策树\n- **选异常 (Business)**：在 Controller/Service 顶层，希望框架自动拦截并返回错误给前端。\n- **选 Result**：在内部复杂业务流转、聚合调用多个外部接口时，希望显式处理每一步的成功与失败。\n\n在这个框架里，两种风格可以并存：\n- 对外接口（Controller）建议用异常风格，交给 `FailFastExceptionHandler` 输出统一 JSON。\n- 对内逻辑（Service/聚合层）建议用 `Result<T>`，把失败当作“数据”来传递和组合。\n\n## 2. 🔗 Result 链式操作\n`Result<T>` 封装了 `Success` 和 `Fail` 两种状态：\n\n- `Result.ok(data)`：成功\n- `Result.fail(code)` / `Result.fail(business)`：失败\n\n```java\nResult<String> result = Result.ok(\"data\")\n    .peek(s -> log.info(\"处理成功: {}\", s))              // 成功副作用，不改变结果\n    .map(String::toUpperCase)                           // 成功值映射\n    .flatMap(s -> Result.ok(s + \"_PROCESSED\"))          // 成功值映射到新的 Result\n    .recover(fail -> \"DEFAULT_VALUE\")                   // 失败降级：把 Fail 变成 Success 值\n    .peekError(fail -> log.warn(\"处理失败: {}\", fail))   // 失败副作用\n    .fold(                                              // 双路映射：把成功/失败都映射到同一类型\n        success -> \"最终成功：\" + success,\n        fail -> \"最终失败：\" + fail.getMessage()\n    );\n```\n\n补充说明：\n- `recover(...)` 的 lambda 需要返回的是 `T`（解包后的值），如果你想返回 `Result<T>`，请用 `recoverWith(...)`。\n- `fold(...)` 返回的是 `Result<R>`，不是直接 `R`（仍然保持“结果容器”的一致性）。\n\n常用小技巧：\n- “把失败变成默认值”：`recover(...)`\n- “把成功值加一层校验”：`filter(predicate, code)`\n\n```java\nResult<Integer> age = Result.ok(16)\n    .filter(a -> a >= 18, Code.AGE_INVALID, \"未成年\");\n```\n\n## 3. 🧰 Results 工具类全览\n`Results` 适合做“批量/集合/副作用”相关的操作（静态工具方法）。\n\n- **`tryOf()`**：捕获旧代码的异常并转为 Result。\n  ```java\n  Result<User> res = Results.tryOf(() -> dao.get(), Code.DB_ERR, \"查询用户失败\");\n  ```\n- **`tryRun()`**：把 `Runnable` 包装成 `Result<Void>`。\n- **`fromOptional()`**：Optional 转 Result。\n- **`sequence()`**：`List<Result<T>>` → `Result<List<T>>`（fail-fast：有一个失败就返回失败）。\n- **`sequenceAll()`**：全量收集版 sequence（失败会聚合成 `MultiBusiness`）。\n- **`traverse()`**：先 map 再 sequence（fail-fast）。\n- **`traverseAll()`**：全量收集版 traverse。\n- **`partition()`**：同时拿到 successes 与 failures（更像“统计报表”）。\n- **`fold()` (针对列表)**：对 `List<Result<T>>` 做归约（Reduce）。\n- **`tap()` / `tapAsync()`**：对 Result 做“非破坏性”副作用（日志、埋点、异步通知等）。\n\n```java\nResult<User> r = Results.tryOf(() -> dao.get(), Code.DB_ERR);\n\nr = Results.tap(r, it -> log.info(\"当前结果: success={}, fail={}\", it.isSuccess(), it.isFail()));\nr = Results.tapAsync(r, it -> metrics.record(it));\n```\n\n## 4. 🌊 与 Java Stream API 集成\n```java\nResult<List<String>> res = Result.ok(List.of(\"a\", \"b\"));\nres.stream().forEach(System.out::println); // 如果是 Fail，stream 会为空，不会报 NPE\n```\n\n## 5. 🧪 常见落地范式（推荐）\n\n### 5.1 外部调用聚合（fail-fast）\n当你有多个外部依赖，任意一个失败就直接失败：\n```java\nResult<User> user = Results.tryOf(() -> userRepo.find(id), Code.DB_ERR);\nResult<Order> order = Results.tryOf(() -> orderRepo.latest(id), Code.DB_ERR);\n\nResult<List<Object>> all = Results.sequence(\n    user.map(u -> (Object) u),\n    order.map(o -> (Object) o)\n);\n```\n\n### 5.2 外部调用聚合（全量收集）\n当你希望“尽可能多拿到信息，同时把失败集中返回”：\n```java\nResult<List<User>> users = Results.traverseAll(ids, uid ->\n    Results.tryOf(() -> userRepo.find(uid), Code.DB_ERR)\n);\n```\n",
            "url": "https://kyriechao.github.io/posts/2026/03/2/26-functional-result",
            "title": "Failure 框架指南",
            "summary": "Spring Boot 参数校验新选择：无缝兼容 JSR-303，支持 Fail-Fast 快速失败与 Fail-Strict 全量收集，提供 Scene+Group 双维度场景校验与统一异常响应。",
            "date_modified": "2026-03-26T03:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/2/26-declarative-validation",
            "content_html": "\n# 🚀 Failure 框架使用教程 - 2. 声明式校验与 Web 层集成（实战主力）\n\n这一篇讲的是“把校验收口到方法入口”：\n- Controller/Service 方法加一个 `@Validate` ✅\n- DTO 用 JSR-303 注解描述基础规则 ✅\n- 复杂业务规则用自定义 Validator 扩展 ✅\n\n---\n\n## 1. 🎛️ `@Validate`：方法级校验总开关\n\n`@Validate` 只能标在 **方法** 上（`@Target(ElementType.METHOD)`），它的作用是：在方法执行前统一完成校验，失败就抛 `Business / MultiBusiness`。\n\n常用参数：\n- `value`：指定自定义校验器（实现 `FastValidator` 或 `TypedValidator`）\n- `fast`：声明式收集模式控制\n    - `true`（默认）：快速失败，只抛第一个错误\n    - `false`：全量收集，汇总多个错误后抛 `MultiBusiness`\n- `scene`：指定业务场景（配合 `@Scene` 过滤字段校验）\n- `groups`：透传给 JSR-303 groups\n\n示例：\n```java\n@PostMapping(\"/users\")\n@Validate(scene = Scenario.CREATE, fast = false)\npublic ApiResponse<String> create(@FailFastBody UserDTO dto) {\n    return ApiResponse.success(\"ok\");\n}\n```\n\n---\n\n## 2. 🎭 `@Scene`：同一套 DTO 的“场景复用”\n\n很多接口会复用 DTO，但不同场景的约束不同：\n- CREATE：用户名/密码必填\n- UPDATE：id 必填，其它字段可选\n\n用法是“字段上标 `@Scene`，方法上用 `@Validate(scene=...)` 指定当前场景”：\n\n```java\npublic class UserDTO {\n    @Scene(Scenario.UPDATE)\n    @NotNull\n    private Long id;\n\n    @Scene({Scenario.CREATE, Scenario.UPDATE})\n    @NotBlank\n    private String username;\n}\n\n@Validate(scene = Scenario.UPDATE)\npublic void update(@RequestBody UserDTO dto) { }\n```\n\n实现语义（很重要）：\n- `@Scene` 只影响 JSR-303 这部分的 violation 过滤\n- 自定义 Validator 是否执行，仍由 `@Validate` 控制\n\n---\n\n## 3. 🛑 `@SkipValidation`：某个参数不参与校验\n\n当方法上有 `@Validate` 时，切面会收集“可校验参数”。如果你希望某个参数完全跳过（例如某个包装对象、或者你就是不想校验它），用 `@SkipValidation`：\n\n```java\n@Validate\npublic void doSomething(UserDTO user, @SkipValidation HttpServletRequest request) { }\n```\n\n---\n\n## 4. 🔍 Scope：集合/嵌套对象校验 + 精确路径\n\n校验集合时，最值钱的是“路径精确到第几个元素的哪个字段”，例如：\n- `[0].name`\n- `users[2].address.city`\n\n### 4.1 List 集合校验（推荐 PathEntry 写法）\n\n```java\nFailure.strict()\n    .notNull(users, ResponseCode.VALIDATION_ERROR_400, \"用户列表不能为空\")\n    .forEach(users, scope -> {\n        PathEntry<String> name = scope.field(UserDTO::getUsername).as(\"name\");\n        PathEntry<String> email = scope.fieldEntry(UserDTO::getEmail);\n        PathEntry<Integer> age = scope.fieldEntry(UserDTO::getAge);\n\n        scope.notBlank(name, ResponseCode.VALIDATION_ERROR_400)\n             .email(email, ResponseCode.VALIDATION_ERROR_400)\n             .positive(age, ResponseCode.VALIDATION_ERROR_400)\n             .done();\n    })\n    .failAll();\n```\n\n为什么要这么写？\n- `field(getter)` 解析 getter 名称在 Java 里不可靠（会回退到默认 `\"field\"`），所以推荐用 `.as(\"name\")` 显式指定展示名\n- `fieldEntry(getter)` 直接返回 `PathEntry<T>`，适合不需要改名的字段\n\n### 4.2 Map 校验与 nested（思路相同）\n- `forEachEntry(...)` 用于 Map 的 value 校验（路径会带上 key）\n- `nested(...)` 用于对象嵌套（路径会自动追加子字段）\n\n---\n\n## 5. 🧬 TypedValidator：一个校验器处理多种类型\n\n当你有多个 DTO/Command，希望统一用一个校验入口，但不同类型规则不同，推荐 `TypedValidator`：\n\n```java\npublic class MyValidator extends TypedValidator {\n    @Override\n    protected void registerValidators() {\n        register(CreateCmd.class, (cmd, ctx) ->\n            Failure.with(ctx)\n                .notBlank(cmd.getName(), Code.NAME_EMPTY)\n                .verify()\n        );\n\n        register(UpdateCmd.class, (cmd, ctx) ->\n            Failure.with(ctx)\n                .notNull(cmd.getId(), Code.ID_EMPTY)\n                .verify()\n        );\n    }\n}\n```\n\n这里用 `.verify()` 而不是 `.fail()`：\n- `.verify()` 只把错误写入 `ctx`\n- 是否抛错、抛一个还是抛多个，由外层 `@Validate(fast=...)` 统一决定\n\n---\n\n## 6. 🧾 `@FailFastBody`：可选 RequestBody（避免 400）\n\n`@FailFastBody` 语义上等同 `@RequestBody`，但支持 `required=false`：\n\n```java\n@PostMapping(\"/search\")\npublic ApiResponse<?> search(@FailFastBody(required = false) QueryDTO dto) {\n    if (dto == null) {\n        return ApiResponse.success(\"no body\");\n    }\n    return ApiResponse.success(dto);\n}\n```\n\n---\n\n## 7. 🧯 统一异常处理与日志定位（IDE 可点击跳转）\n\n失败时会返回统一 JSON，且日志中会带上 `(SomeClass.java:34)` 这种可点击定位信息，排查体验非常好。\n\nTraceId 说明：\n- 默认关闭（`fail-fast.trace-id.enabled=false`）：响应体不返回 `traceId`\n- 显式开启（`enabled=true`）：响应体/响应头/MDC 按配置生效\n\n---\n\n## 8. 🧩 OpenAPI（Swagger）集成\n\n如果你需要让 Swagger UI 展示统一错误结构，可以引入 `failure-openapi-springdoc-starter` 相关模块进行增强。\n\n注意事项：\n- Springdoc 与 Spring Boot 的版本要匹配，避免传递依赖引入不兼容的 Boot 版本导致启动异常。\n",
            "url": "https://kyriechao.github.io/posts/2026/03/2/26-declarative-validation",
            "title": "Failure 框架指南",
            "summary": "Spring Boot 参数校验新选择：无缝兼容 JSR-303，支持 Fail-Fast 快速失败与 Fail-Strict 全量收集，提供 Scene+Group 双维度场景校验与统一异常响应。",
            "date_modified": "2026-03-26T02:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/2/26-programmatic-validation",
            "content_html": "\n# 🚀 Failure 框架使用教程 - 1. 编程式链式校验（核心基础）\n\n当你需要在 Service 层手写复杂的校验逻辑时，Failure 提供了极度流畅的链式 API。\n\n这一篇只讲“编程式校验”的核心：用 `Failure.begin()/strict()` 直接在代码里把规则写清楚，并用统一的异常模型（`Business/MultiBusiness`）把错误抛出去。\n\n## 1. 🏃‍♂️ 两种校验哲学：begin() vs strict()\n\n- **`Failure.begin()`（快速失败 Fail-Fast）** 🏎️\n    - **行为**：遇到第一个不满足的条件，立即终止链式调用并抛出 `Business` 异常。\n    - **场景**：性能敏感的接口、或者表单填写时前端只需要逐项提示的场景。\n\n- **`Failure.strict()`（全量收集 Fail-Strict）** 🕵️‍♂️\n    - **行为**：即使某个条件失败，也会继续执行后续校验，最后统一抛出包含所有错误的 `MultiBusiness` 异常。\n    - **场景**：希望一次性告诉前端所有填错的字段，提升用户体验。\n\n### 1.1 终结方法速查（很重要）\n链式调用最后一定要“收口”，否则只是在描述规则，不会真正触发抛错：\n\n- `Failure.begin() ... .fail()`：快速失败（抛 `Business`）\n- `Failure.strict() ... .failAll()`：全量收集（抛 `MultiBusiness`）\n- `Failure.with(ctx) ... .verify()`：只写入上下文，不直接抛错（通常用于切面托管）\n\n## 2. 🛠️ 链式 API 完整清单与自定义规则\n\n除了常用的 `notNull`, `notBlank`, `positive`，你还可以使用高阶扩展：\n\n- **`check(boolean condition, Code code)`**：传入任意布尔表达式。\n- **自定义 Predicate `satisfies()`**：\n  ```java\n  Failure.begin()\n      .satisfies(str, s -> s.startsWith(\"A\"), ErrorCode.MUST_START_WITH_A)\n      .fail();\n  ```\n- **控制流：`when()` 与 `resume()` 状态开关**：\n  你可以通过 `when(boolean)` 动态开启或关闭后续的校验逻辑：\n  ```java\n  Failure.begin()\n      .when(user.isVip()) // 如果不是 VIP，后续校验将被忽略\n      .notNull(user.getVipLevel(), Code.VIP_LEVEL_REQUIRED)\n      .resume()           // 恢复全量校验模式\n      .notBlank(user.getUsername(), Code.NAME_BLANK)\n      .fail();\n  ```\n\n### 2.1 常用方法速查（字符串）\n如果你在写“用户注册/登录”一类的校验，这些会非常常见：\n\n- `notBlank(str)` / `blank(str)`\n- `lengthBetween(str, min, max)`\n- `match(str, regex)`\n- `email(email)`\n- `startsWith/endsWith/contains`\n\n示例（完全符合当前代码实现）：\n```java\nFailure.begin()\n       .notBlank(email, Code.EMAIL_EMPTY)\n       .email(email, Code.EMAIL_INVALID)\n       .lengthBetween(password, 8, 32, Code.PASSWORD_LENGTH)\n       .fail();\n```\n\n## 3. 🚦 短路逻辑控制\n在 `strict()` 模式下，有时你希望某个关键错误发生后**局部短路**：\n```java\nFailure.strict()\n       .notNull(user, Code.USER_NULL)\n       .stopOnFail() // 如果 user 是 null，立刻停下，防止下面的 getUsername 报 NPE！\n       .notBlank(user.getUsername(), Code.NAME_BLANK)\n       .resume()     // 恢复全量收集模式\n       .failAll();\n```\n同时支持组合条件：`.or(chain -> chain.notNull(a).notNull(b))` 满足其一即可。\n\n## 4. 🧳 延迟计算 (defer)\n如果你需要在校验链中执行一些耗时的数据库查询，可以使用 `defer`，它只在前面的校验都通过时才会执行：\n```java\nFailure.begin()\n       .notBlank(user.getUsername(), Code.NAME_BLANK)\n       .defer(() -> userService.findByUsername(user.getUsername()) == null, Code.USER_EXISTS)\n       .fail();\n```\n\n## 5. 🧩 上下文复用与终结方法对比\n在普通的 Service/Controller 中，你通常会使用 `.fail()`（快速失败）或 `.failAll()`（全量收集）来**直接抛出异常**。\n但当你编写被 AOP 切面托管的自定义校验器时，你需要复用切面传进来的上下文：\n\n- **`Failure.with(ctx)`**：基于现有的 `ValidationContext` 启动校验，继承外层的收集模式（fast/strict）。\n- **`verify()` vs `fail()`**：\n    - `.fail() / .failAll()`：**立即抛出** `Business / MultiBusiness` 异常，中断当前线程。\n    - `.verify()`：**不抛出异常**，它仅仅是将错误写入 `ctx` 并触发监控埋点。异常抛出的动作将由外层的 `@Validate` 切面统一接管。\n\n```java\n// 在自定义校验器中\npublic void validate(User user, ValidationContext ctx) {\n    Failure.with(ctx) // 复用上下文\n           .notBlank(user.getName(), Code.NAME_EMPTY)\n           .verify(); // 仅记录错误，不抛出异常\n}\n```\n\n### 5.1 如何选 verify / fail（一句话）\n- 你自己写的业务流程里：选 `fail()/failAll()`（立刻抛错，控制流清晰）。\n- 你写的是被 `@Validate` 触发的校验器：选 `verify()`（把“抛错权”交给切面统一处理）。\n",
            "url": "https://kyriechao.github.io/posts/2026/03/2/26-programmatic-validation",
            "title": "Failure 框架指南",
            "summary": "Spring Boot 参数校验新选择：无缝兼容 JSR-303，支持 Fail-Fast 快速失败与 Fail-Strict 全量收集，提供 Scene+Group 双维度场景校验与统一异常响应。",
            "date_modified": "2026-03-26T01:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/2/26-quick-start",
            "content_html": "\n# 🚀 Failure 框架使用教程 - 0. 前置准备（快速开始）\n\n这一篇目标只有一个：让你在一个 Spring Boot 项目里 **5 分钟跑通 Failure** ✅\n\n---\n\n## 1. 🧰 环境要求\n- **JDK**：17+\n- **Spring Boot**：3.x\n- **Web 技术栈**：Spring MVC（Servlet）。当前 Starter 的 Web 增强能力（例如 `@FailFastBody`、TraceId Filter）基于 Servlet 体系。\n\n---\n\n## 2. 📦 引入依赖（Maven）\n\n```xml\n<dependency>\n    <groupId>io.github.kyriechao</groupId>\n    <artifactId>failure-spring-boot-starter</artifactId>\n    <version>1.1.1</version>\n</dependency>\n```\n\n如果你还没有引入参数校验依赖，建议同时引入：\n```xml\n<dependency>\n    <groupId>org.springframework.boot</groupId>\n    <artifactId>spring-boot-starter-validation</artifactId>\n</dependency>\n```\n\n---\n\n## 3. ⚙️ 自动配置会帮你做什么\n\n引入 Starter 后，自动配置类会把关键能力装配进 Spring 容器（无需手工注册）：\n- `@Validate` 的 AOP 切面：统一触发 JSR-303 + 自定义 Validator\n- `@FailFastBody` 的参数解析增强：支持 “可选 body”\n- 默认异常处理：统一输出 JSON（`Business / MultiBusiness / JSR-303` 异常都会被处理）\n\n---\n\n## 4. 🧪 第一个可运行示例（编程式 vs 声明式）\n\n下面给你一套“对照组”，看完你就能分清两种使用方式：\n\n### 4.1 编程式：Service 里链式校验（推荐用于复杂业务规则）\n\n```java\nFailure.begin()\n       .notBlank(dto.getName(), Code.NAME_EMPTY)\n       .positive(dto.getAge(), Code.AGE_INVALID)\n       .fail(); // 触发抛错（fail-fast）\n```\n\n如果你希望一次性返回多个错误（全量收集）：\n```java\nFailure.strict()\n       .notBlank(dto.getName(), Code.NAME_EMPTY)\n       .positive(dto.getAge(), Code.AGE_INVALID)\n       .failAll();\n```\n\n### 4.2 声明式：Controller/Service 方法上 `@Validate`\n\n```java\n@PostMapping(\"/users\")\n@Validate(scene = Scenario.CREATE, fast = false)\npublic ApiResponse<String> create(@FailFastBody UserDTO dto) {\n    return ApiResponse.success(\"ok\");\n}\n```\n\n它会做两件事：\n1) 先走 `jakarta.validation`（DTO 上的 `@NotNull/@NotBlank/...`）\n2) 再走你指定的自定义校验器（`FastValidator` / `TypedValidator`）\n\n---\n\n## 5. 📤 失败时会返回什么\n\n失败时默认会返回统一 JSON（字段可能会随 verbose/trace-id 配置有所变化）：\n\n```json\n{\n  \"code\": 400,\n  \"message\": \"参数校验失败\",\n  \"description\": \"参数校验失败\",\n  \"errors\": [\n    {\n      \"path\": \"name\",\n      \"code\": 400,\n      \"rejected\": \"\",\n      \"detail\": \"不能为空\",\n      \"message\": \"参数校验失败\"\n    }\n  ],\n  \"timestamp\": \"2026-03-26 15:17:59\"\n}\n```\n\n---\n\n## 6. 🧾 TraceId（默认关闭，按需开启）\n\n如果你希望前端拿到 `traceId` 用于反查日志，可以开启：\n```yaml\nfail-fast:\n  trace-id:\n    enabled: true\n    header-name: X-Trace-Id\n    response-header: true\n    response-header-name: X-Trace-Id\n    mdc-enabled: true\n    mdc-key: traceId\n    generate-if-missing: true\n```\n\n如果你显式关闭：\n```yaml\nfail-fast:\n  trace-id:\n    enabled: false\n```\n则异常响应体将不返回 `traceId` 字段（“关掉就是彻底没有”）。\n\n\n",
            "url": "https://kyriechao.github.io/posts/2026/03/2/26-quick-start",
            "title": "Failure 框架指南",
            "summary": "Spring Boot 参数校验新选择：无缝兼容 JSR-303，支持 Fail-Fast 快速失败与 Fail-Strict 全量收集，提供 Scene+Group 双维度场景校验与统一异常响应。",
            "date_modified": "2026-03-26T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/1/12-math-notes",
            "content_html": "\n# 傅里叶变换基础\n\n本文主要测试博客对 LaTeX 数学公式的支持情况。\n\n## 欧拉公式\n\n首先回顾一下著名的欧拉公式：\n\n$$\ne^{ix} = \\cos x + i\\sin x\n$$\n\n这个公式被费曼称为\"数学中最卓越的公式\"。\n\n## 傅里叶变换定义\n\n对于一个连续时间信号 $x(t)$，其傅里叶变换 $X(\\omega)$ 定义为：\n\n$$\nX(\\omega) = \\int_{-\\infty}^{\\infty} x(t) e^{-j\\omega t} dt\n$$\n\n逆变换定义为：\n\n$$\nx(t) = \\frac{1}{2\\pi} \\int_{-\\infty}^{\\infty} X(\\omega) e^{j\\omega t} d\\omega\n$$\n\n### 离散傅里叶变换 (DFT)\n\n在计算机处理中，我们使用离散形式。对于长度为 $N$ 的序列 $x[n]$：\n\n$$\nX[k] = \\sum_{n=0}^{N-1} x[n] e^{-j\\frac{2\\pi}{N}kn}, \\quad k = 0, 1, \\dots, N-1\n$$\n\n## 矩阵运算示例\n\n$$\nA = \\begin{pmatrix}\na & b \\\\\nc & d\n\\end{pmatrix}, \\quad\nA^{-1} = \\frac{1}{ad-bc} \\begin{pmatrix}\nd & -b \\\\\n-c & a\n\\end{pmatrix}\n$$\n\n## 总结\n\n如果上面的公式都能正常显示，说明我们的 KaTeX 配置非常成功！🎉\n",
            "url": "https://kyriechao.github.io/posts/2026/03/1/12-math-notes",
            "title": "数学笔记：傅里叶变换基础",
            "summary": "\n 傅里叶变换基础\n\n本文主要测试博客对 LaTeX 数学公式的支持情况。\n\n 欧拉公式\n\n首先回顾一下著名的欧拉公式：\n\n$$\ne^{ix} = \\cos x + i\\sin x\n$$\n\n这个公式被费曼称为\"数学中最卓越的公式\"。\n\n 傅里叶变换定义\n\n对于一个连续时间信号 $x(t)$...",
            "date_modified": "2026-03-12T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/1/11-java-operators-and-control-flow",
            "content_html": "\nHello 我来继续介绍\n\n## 1.运算符（Operators）\nJava 提供了丰富的运算符来处理变量和值。掌握它们是编写逻辑代码的基础。\n\n### 1.1 算术运算符\n\n用于基本数学运算：\n\n| 运算符 | 说明       | 示例            |\n|--------|------------|-----------------|\n| `+`    | 加法       | `5 + 3 → 8`     |\n| `-`    | 减法       | `5 - 3 → 2`     |\n| `*`    | 乘法       | `5 * 3 → 15`    |\n| `/`    | 除法       | `5 / 2 → 2`（整数除法！） |\n| `%`    | 取余（模） | `5 % 2 → 1`     |\n| `++`   | 自增 1     | `x++` 或 `++x`  |\n| `--`   | 自减 1     | `x--` 或 `--x`  |\n\n> 💡 注意：\n> - 整数相除结果仍是整数（小数部分直接丢弃）。\n> - `++x`（先加后用） vs `x++`（先用后加）：\n\n```java\nint a = 5;\nint b = a++;  // b = 5, a = 6\nint c = ++a;  // a = 7, c = 7\n```\n\n---\n\n### 1.2 赋值运算符\n\n将右边的值赋给左边的变量：\n\n| 运算符 | 等价写法        | 说明         |\n|--------|----------------|--------------|\n| `=`    | `x = 5`        | 基本赋值      |\n| `+=`   | `x = x + 5`    | 加后赋值      |\n| `-=`   | `x = x - 5`    | 减后赋值      |\n| `*=`   | `x = x * 5`    | 乘后赋值      |\n| `/=`   | `x = x / 5`    | 除后赋值      |\n| `%=`   | `x = x % 5`    | 取余后赋值    |\n\n```java\nint count = 10;\ncount += 3;  // 相当于 count = count + 3 → 13\n```\n\n---\n\n### 1.3 比较运算符\n\n用于比较两个值，**结果总是 `boolean`（true/false）**：\n\n| 运算符 | 含义     | 示例             |\n|--------|----------|------------------|\n| `==`   | 等于     | `5 == 5 → true`  |\n| `!=`   | 不等于   | `5 != 3 → true`  |\n| `>`    | 大于     | `5 > 3 → true`   |\n| `<`    | 小于     | `5 < 3 → false`  |\n| `>=`   | 大于等于 | `5 >= 5 → true`  |\n| `<=`   | 小于等于 | `5 <= 3 → false` |\n\n> ⚠️ **重要区别：`==` 对基本类型 vs 引用类型**\n>\n> - **基本类型**：比较的是**值**\n>   ```java\n>   int a = 100;\n>   int b = 100;\n>   System.out.println(a == b); // true\n>   ```\n> - **引用类型（如 String）**：比较的是**内存地址**（是否同一个对象）\n>   ```java\n>   String s1 = new String(\"hello\");\n>   String s2 = new String(\"hello\");\n>   System.out.println(s1 == s2);      // false（不同对象）\n>   System.out.println(s1.equals(s2)); // true（内容相同）\n>   ```\n> ✅ **记住：比较字符串内容请用 `.equals()`，不是 `==`！**\n\n---\n\n### 1.4 逻辑运算符\n\n用于组合多个布尔表达式：\n\n| 运算符    | 名称       | 特性                 | 示例                    |\n|--------|------------|--------------------|-------------------------|\n| `&&`   | 逻辑与     | **短路**：左为 false 则不计算右边 | `(x > 0) && (y < 10)`   |\n| `\\|\\|` | 逻辑或     | **短路**：左为 true 则不计算右边  | `(x == null) || x.isEmpty()`  |\n| `!`    | 逻辑非     | 取反                 | `!(age < 18)`           |\n\n> 💡 **短路特性实战：避免空指针**\n>\n> ```java\n> String str = null;\n> // 如果 str 是 null，str.length() 会抛异常！\n> // 但用 && 可以安全判断：\n> if (str != null && str.length() > 0) {\n>     System.out.println(\"非空字符串\");\n> }\n> // 因为 str != null 为 false，右边根本不会执行！\n> ```\n\n---\n\n### 1.5 位运算符（简要了解）\n\n操作二进制位，常用于底层开发或性能优化（初学者知道即可）：\n\n| 运算符 | 说明       | 示例（假设 a=5(101), b=3(011)） |\n|--------|------------|----------------------------------|\n| `&`    | 按位与     | `a & b → 1`（001）              |\n| `\\|`   | 按位或     | `a \\| b → 7`（111）             |\n| `^`    | 按位异或   | `a ^ b → 6`（110）              |\n| `~`    | 按位取反   | `~a → -6`（补码表示）           |\n| `<<`   | 左移       | `a << 1 → 10`（相当于 ×2）      |\n| `>>`   | 右移（带符号）| `a >> 1 → 2`（相当于 ÷2）     |\n| `>>>`  | 无符号右移 | 忽略符号位，高位补 0            |\n\n> 📌 初学可跳过，面试或系统编程时再深入。\n\n---\n\n### 1.6 三元运算符（条件运算符）\n\n简洁的 `if-else` 写法：\n\n```java\n// 语法：条件 ? 表达式1 : 表达式2\nString result = (score >= 60) ? \"及格\" : \"不及格\";\n```\n\n> 💡 **示例：判断奇偶数**\n>\n> ```java\n> int num = 7;\n> String parity = (num % 2 == 0) ? \"偶数\" : \"奇数\";\n> System.out.println(num + \" 是 \" + parity); // 7 是 奇数\n> ```\n\n> ✅ 优点：代码简短  \n> ❌ 缺点：嵌套多层会难读（建议只用于简单判断）\n\n---\n\n\n好的！以下是 **“流程控制（Control Flow）”** 的完整 Markdown 内容，从 **第 2 节开始编号**（即 `## 2. 流程控制`），风格与你第一篇完全一致：零基础友好、代码清晰、带实用提示和常见坑点。\n\n你可以直接复制粘贴到你的第二篇教程中。\n\n---\n\n## 2. 流程控制（Control Flow）\n\n程序默认从上到下逐行执行。但现实中的逻辑往往需要**根据条件跳过某些代码**，或**重复执行一段操作**——这就是流程控制的作用。\n\nJava 提供了三种主要的流程控制结构：**条件判断**（if / switch）、**循环**（for / while / do-while）。\n\n---\n\n### 2.1 条件语句：`if` / `else if` / `else`\n\n根据布尔表达式的真假决定是否执行某段代码。\n\n```java\nint age = 20;\n\nif (age < 13) {\n    System.out.println(\"儿童\");\n} else if (age < 18) {\n    System.out.println(\"青少年\");\n} else {\n    System.out.println(\"成年人\");\n}\n// 输出：成年人\n```\n\n> ✅ **最佳实践**：\n> - 条件尽量简单，复杂逻辑可提取为变量提高可读性：\n>   ```java\n>   boolean isTeenager = age >= 13 && age < 18;\n>   if (isTeenager) {\n>       System.out.println(\"青少年\");\n>   }\n>   ```\n> - 避免深层嵌套，尽早 `return` 或使用卫语句（guard clause）。\n\n---\n\n### 2.2 多分支选择：`switch`\n\n当需要根据**一个变量的多个可能值**做不同处理时，用 `switch` 更清晰。\n\n#### 传统写法（适用于所有 Java 版本）\n```java\nchar grade = 'B';\n\nswitch (grade) {\n    case 'A':\n        System.out.println(\"优秀\");\n        break;  // 必须加！否则会“穿透”到下一个 case\n    case 'B':\n        System.out.println(\"良好\");\n        break;\n    case 'C':\n        System.out.println(\"及格\");\n        break;\n    default:\n        System.out.println(\"未知等级\");\n}\n// 输出：良好\n```\n\n> ⚠️ **注意**：\n> - `case` 后只能是**常量**（字面量或 `final` 变量）。\n> - 忘记写 `break` 会导致 **fall-through（穿透）**，通常是个 bug！\n\n#### Java 14+ 新语法（更安全简洁，可选了解）\n```java\n// 使用 -> 代替 break，自动跳出\nswitch (grade) {\n    case 'A' -> System.out.println(\"优秀\");\n    case 'B' -> System.out.println(\"良好\");\n    case 'C' -> System.out.println(\"及格\");\n    default -> System.out.println(\"未知等级\");\n}\n```\n\n> 💡 **支持的类型**：`byte`/`short`/`int`/`char`、`String`、枚举（`enum`）  \n> （不支持 `long`、`float`、`double`、`boolean`）\n\n---\n\n### 2.3 循环语句\n\n#### 2.3.1 `for` 循环（已知循环次数时首选）\n\n```java\n// 打印 1 到 5\nfor (int i = 1; i <= 5; i++) {\n    System.out.println(\"第 \" + i + \" 次\");\n}\n```\n\n- **初始化**：`int i = 1`（只执行一次）\n- **条件判断**：`i <= 5`（每次循环前检查）\n- **更新操作**：`i++`（每次循环后执行）\n\n> 💡 小技巧：想倒序？`for (int i = 5; i >= 1; i--)`\n\n#### 2.3.2 增强 `for` 循环（遍历数组或集合）\n\n```java\nint[] numbers = {10, 20, 30};\n\nfor (int num : numbers) {\n    System.out.println(num);\n}\n// 输出：10 20 30\n```\n\n> ✅ 优点：代码简洁，不易出错  \n> ❌ 缺点：无法获取索引，不能修改原数组元素（但可读）\n\n#### 2.3.3 `while` 循环（条件为真时重复）\n\n```java\nint count = 3;\nwhile (count > 0) {\n    System.out.println(\"倒计时：\" + count);\n    count--;\n}\n// 输出：倒计时：3 → 2 → 1\n```\n\n> ⚠️ **小心死循环**！确保循环体内有改变条件的语句。\n\n#### 2.3.4 `do-while` 循环（至少执行一次）\n\n```java\nimport java.util.Scanner;\n\nScanner sc = new Scanner(System.in);\nint input;\n\ndo {\n    System.out.print(\"请输入一个正数: \");\n    input = sc.nextInt();\n} while (input <= 0);\n\nSystem.out.println(\"你输入的是: \" + input);\n```\n\n> ✅ 适用场景：菜单选择、用户输入验证等需要**先执行再判断**的情况。\n\n---\n\n### 2.4 控制循环：`break` 与 `continue`\n\n- `break`：**立即退出整个循环**\n- `continue`：**跳过本次循环剩余代码，进入下一次**\n\n```java\nfor (int i = 1; i <= 5; i++) {\n    if (i == 3) {\n        continue;  // 跳过 i=3\n    }\n    if (i == 5) {\n        break;     // 到 i=5 时退出循环\n    }\n    System.out.println(i);\n}\n// 输出：1 2 4\n```\n\n> 💡 **标签（label）跳转**（高级用法，了解即可）：\n> ```java\n> outer: for (int i = 0; i < 3; i++) {\n>     for (int j = 0; j < 3; j++) {\n>         if (i == 1 && j == 1) {\n>             break outer; // 跳出外层循环\n>         }\n>         System.out.println(i + \",\" + j);\n>     }\n> }\n> ```\n\n---\n\n### 2.5 常见误区与调试技巧\n\n| 问题 | 错误示例 | 正确做法                    |\n|------|--------|-------------------------|\n| 忘记大括号 | `if (x > 0) System.out.println(\"正\"); x++;` | 虽然一行不会错但是加了更清楚 `{}`     |\n| `==` 比较字符串 | `if (str == \"hello\")` | 用 `str.equals(\"hello\")` |\n| 数组越界 | `for (int i=0; i<=arr.length; i++)` | 条件应为 `i < arr.length`   |\n| 死循环 | `while (true) { ... }`（无退出条件） | 确保有 `break` 或条件变化       |\n\n> 🔍 **调试建议**：在循环中打印变量值，观察变化过程。\n\n---",
            "url": "https://kyriechao.github.io/posts/2026/03/1/11-java-operators-and-control-flow",
            "title": "Java 零基础入门：从 Hello World 到核心语法",
            "summary": "\nHello 我来继续介绍\n\n 1.运算符（Operators）\nJava 提供了丰富的运算符来处理变量和值。掌握它们是编写逻辑代码的基础。\n\n 1.1 算术运算符\n\n用于基本数学运算：\n\n| 运算符 | 说明       | 示例            |\n|--------|-----...",
            "date_modified": "2026-03-11T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/1/11-python-data-analysis",
            "content_html": "\n# Python 数据分析入门：Pandas 实战\n\nPandas 是 Python 中最流行的数据分析库。它提供了名为 DataFrame 的强大数据结构，让数据操作变得异常简单。\n\n## 安装\n\n```bash\npip install pandas numpy\n```\n\n## 基础操作\n\n### 1. 创建 DataFrame\n\n```python\nimport pandas as pd\nimport numpy as np\n\ndata = {\n    'Name': ['Alice', 'Bob', 'Charlie', 'David'],\n    'Age': [24, 27, 22, 32],\n    'City': ['New York', 'Los Angeles', 'Chicago', 'Houston']\n}\n\ndf = pd.DataFrame(data)\nprint(df)\n```\n\n输出结果：\n```text\n      Name  Age         City\n0    Alice   24     New York\n1      Bob   27  Los Angeles\n2  Charlie   22      Chicago\n3    David   32      Houston\n```\n\n### 2. 数据筛选\n\n我们可以轻松筛选出满足条件的数据：\n\n```python\n# 筛选年龄大于 25 的人\nolder_than_25 = df[df['Age'] > 25]\n\nprint(older_than_25)\n```\n\n### 3. 数据统计\n\nPandas 提供了丰富的统计方法：\n\n```python\nprint(f\"平均年龄: {df['Age'].mean()}\")\nprint(f\"最大年龄: {df['Age'].max()}\")\n```\n\n## 进阶技巧：分组与聚合\n\n```python\n# 按城市分组并计算平均年龄（示例数据量太小，仅作演示）\ngrouped = df.groupby('City')['Age'].mean()\n```\n\n## 结语\n\nPandas 的功能远不止于此。配合 Matplotlib 或 Seaborn，你还可以轻松画出精美的图表。\n\n> **提示**: 在处理大型数据集时，注意内存的使用情况。\n",
            "url": "https://kyriechao.github.io/posts/2026/03/1/11-python-data-analysis",
            "title": "Python 数据分析入门：Pandas 实战",
            "summary": "\n Python 数据分析入门：Pandas 实战\n\nPandas 是 Python 中最流行的数据分析库。它提供了名为 DataFrame 的强大数据结构，让数据操作变得异常简单。\n\n 安装\n\nbash\npip install pandas numpy\n\n\n 基础操作\n\n...",
            "date_modified": "2026-03-11T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/1/10-nextjs-tutorial-2",
            "content_html": "\n# Next.js 14 实战系列 (二)\n\n在上一篇文章中，我们搭建了基础环境。今天我们来聊聊 Next.js 的核心特性之一：**路由系统**。\n\n## 1. 静态路由\n\n在 `app` 目录下创建一个文件夹 `about`，并在其中新建 `page.tsx`：\n\n```tsx\n// app/about/page.tsx\nexport default function About() {\n  return <h1>关于我们</h1>;\n}\n```\n\n现在访问 `/about`，Next.js 会自动渲染这个组件。这就是**文件系统路由**的魔力。\n\n## 2. 动态路由\n\n如果我们需要为每篇博客文章生成一个页面，比如 `/posts/1`、`/posts/2`，就需要用到动态路由。\n\n创建 `app/posts/[id]/page.tsx`：\n\n```tsx\nexport default function Post({ params }: { params: { id: string } }) {\n  return <div>Post ID: {params.id}</div>;\n}\n```\n\n## 3. 嵌套布局 (Nested Layouts)\n\nNext.js 允许你为特定的路由段定义布局。例如，你可以为博客部分创建一个侧边栏布局，而不影响首页。\n\n```tsx\n// app/blog/layout.tsx\nexport default function BlogLayout({\n  children,\n}: {\n  children: React.ReactNode\n}) {\n  return (\n    <section>\n      <nav>Blog Navigation</nav>\n      {children}\n    </section>\n  )\n}\n```\n\n### 总结\n\n- `page.tsx` 定义路由的 UI。\n- `layout.tsx` 定义共享 UI。\n- `[folderName]` 创建动态路由段。\n\n掌握这些规则，你就能构建出复杂的应用结构了。下一章我们将讨论数据获取 (Data Fetching)。\n",
            "url": "https://kyriechao.github.io/posts/2026/03/1/10-nextjs-tutorial-2",
            "title": "Next.js 14 实战系列 (二)：深入理解路由与布局",
            "summary": "\n Next.js 14 实战系列 (二)\n\n在上一篇文章中，我们搭建了基础环境。今天我们来聊聊 Next.js 的核心特性之一：路由系统。\n\n 1. 静态路由\n\n在 app 目录下创建一个文件夹 about，并在其中新建 page.tsx：\n\ntsx\n// app/...",
            "date_modified": "2026-03-10T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/0/09-nextjs-tutorial-1",
            "content_html": "\n# Next.js 14 实战系列 (一)\n\n欢迎来到 Next.js 学习笔记的第一篇！在本系列中，我们将一起探索 Next.js 14 的强大功能，特别是 App Router 和服务器组件 (Server Components)。\n\n## 1. 环境准备\n\n首先，确保你已经安装了 Node.js 18.17 或更高版本。\n\n```bash\nnode -v\n# v18.17.0\n```\n\n## 2. 创建项目\n\n使用 `create-next-app` 脚手架快速启动项目：\n\n```bash\nnpx create-next-app@latest my-blog\n```\n\n在交互式命令行中，我们选择以下配置：\n- TypeScript: **Yes**\n- Tailwind CSS: **Yes**\n- App Router: **Yes**\n- ESLint: **Yes**\n\n## 3. 项目结构概览\n\n创建完成后，你的项目目录应该是这样的：\n\n```\nmy-blog/\n├── app/\n│   ├── layout.tsx    # 根布局\n│   └── page.tsx      # 首页\n├── public/           # 静态资源\n├── tailwind.config.ts\n└── package.json\n```\n\n## 4. 编写第一个页面\n\n打开 `app/page.tsx`，修改内容如下：\n\n```tsx\nexport default function Home() {\n  return (\n    <main className=\"flex min-h-screen flex-col items-center justify-between p-24\">\n      <h1 className=\"text-4xl font-bold\">Hello, Next.js 14!</h1>\n    </main>\n  );\n}\n```\n\n运行 `npm run dev`，打开浏览器访问 `http://localhost:3000`，你应该能看到我们的新页面了。\n\n---\n\n下一篇文章中，我们将学习如何添加 Markdown 支持来渲染博客文章。敬请期待！\n",
            "url": "https://kyriechao.github.io/posts/2026/03/0/09-nextjs-tutorial-1",
            "title": "Next.js 14 实战系列 (一)：从零开始搭建博客",
            "summary": "这是 Next.js 学习系列的第一篇文章，我们将介绍如何配置开发环境并创建一个基础的 Next.js 项目。",
            "date_modified": "2026-03-09T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/0/10-java-basics",
            "content_html": "\n欢迎来到 Java 学习笔记的第一篇！在本系列中，我们将从零开始，一步步掌握 Java 的核心语法。<br/>\n不跳过任何基础，从最简单的 `System.out.println` 讲起，带你真正理解输开发中必备的知识。\n\n## 1. 环境准备\n首先，确保你已经安装了 Java 8 或更高版本。\n\n```bash\njava --version\n# java 17.0.16 2025-07-15 LTS\n```\n\n## 2. 创建项目\n\n使用 `javac` 编译你的第一个 Java 程序：\n\n用JetBrains IDEA创建一个Java项目(first-demo)，并创建一个名为`Main.java`的文件。\n\n```\nfirst-demo/\n├── src\n│    └── Main.java           # 入口类\n└── out/                     # 编译输出（自动创建）\n```\n## 3. 编写代码\n```java\npublic class Main {\n    public static void main(String[] args) {\n        System.out.println(\"Hello, Java!\");\n    }\n}\n```\n\n## 4. 编译与运行\n- 可以点击JetBrains IDEA的Current File 打印台即可出现Hello, Java!\n\n- 也可以使用命令行编译和运行：\n```bash\n# 编译\njavac src/Main.java -d out\n\n# 运行\njava -cp out Main\n```\n## 5. 运行结果\n```\nHello, Java!\n```\n\n## 6. 数据类型与变量\n\nJava 是**强类型语言**，每个变量必须先声明类型。数据类型分为**基本类型**（8种）和**引用类型**（数组、类、接口等）。\n\n### 6.1 完整示例\n\n```java\nimport java.util.Arrays;\n\npublic class Main {\n    public static void main(String[] args) {\n        // 整数类型（从小到大）\n        byte score = 90;               //  8 bit  -128 ~ 127\n        short height = 170;            // 16 bit -32768 ~ 32767\n        int age = 25;                  // 32 bit  ±21 亿（最常用）\n        long population = 1_000_000L;  // 64 bit  ±9×10¹⁸\n\n        // 浮点类型\n        float weight = 68.5f;          // 32 bit，必须加 f\n        double price = 19.99;          // 64 bit（默认，精度更高）\n\n        // 字符与布尔\n        char grade = 'A';              // 16 bit，单引号，存一个字符\n        boolean isStudent = true;      // true 或 false\n\n        // 引用类型\n        String name = \"Alice\";         // 字符串，双引号，final class\n        int[] arr = {1, 2, 3, 4, 5};   // 数组，引用类型\n\n        // 特殊表示法\n        int bin = 0b1010;              // 二进制（0b 开头）\n        int hex = 0xFF;                // 十六进制（0x 开头）\n        double pi = 3.141_592_653;     // 下划线分隔，提升可读性\n        String emoji = \"\\uD83D\\uDE04\"; // Unicode 表情 😄\n        char chinese = '中';           // 支持中文\n\n        // var 类型推断（Java 10+）\n        var message = \"Hello\";         // 编译器推断为 String\n\n        // 打印输出\n        System.out.println(\"Score: \" + score);\n        System.out.println(\"Height: \" + height);\n        System.out.println(\"Age: \" + age);\n        System.out.println(\"Population: \" + population);\n        System.out.println(\"Weight: \" + weight);\n        System.out.println(\"Price: \" + price);\n        System.out.println(\"Grade: \" + grade);\n        System.out.println(\"Is Student: \" + isStudent);\n        System.out.println(\"Name: \" + name);\n        System.out.println(\"Array: \" + Arrays.toString(arr)); // 注意这里\n        System.out.println(\"Binary 0b1010 = \" + bin);\n        System.out.println(\"Hex 0xFF = \" + hex);\n        System.out.println(\"Pi = \" + pi);\n        System.out.println(\"Emoji: \" + emoji);\n        System.out.println(\"Chinese: \" + chinese);\n        System.out.println(\"Var: \" + message);\n    }\n}\n```\n\n### 6.2 关键点总结\n\n| 类型        | 大小     | 范围/说明          | 注意            |\n| :-------- | :----- | :------------- | :------------ |\n| `byte`    | 8 bit  | -128 ~ 127     | 文件流、节省内存      |\n| `short`   | 16 bit | -32768 ~ 32767 | 很少用           |\n| `int`     | 32 bit | ±21亿           | 整数默认类型        |\n| `long`    | 64 bit | 很大             | 赋值加 `L`       |\n| `float`   | 32 bit | 6~7位精度         | 赋值加 `f`       |\n| `double`  | 64 bit | 15~16位精度       | 浮点默认类型        |\n| `char`    | 16 bit | 0 ~ 65535      | 单引号，存 Unicode |\n| `boolean` | 1 bit  | true/false     | 不能转整数         |\n\n### 6.3 常见坑\n```java\n// ❌ 错误：整数默认是 int，超出范围要加 L\nlong big = 3000000000;     // 编译错误！30亿超过 int 范围\nlong big = 3000000000L;    // ✅ 正确\n\n// ❌ 错误：浮点默认是 double，不能直接赋给 float\nfloat f = 3.14;            // 编译错误！\nfloat f = 3.14f;           // ✅ 正确\n\n// ❌ 错误：char 用双引号\nchar c = \"A\";              // 编译错误！String 不能赋给 char\nchar c = 'A';              // ✅ 正确\n\n// ⚠️ 注意：数组直接打印是地址\nSystem.out.println(arr);   // [I@6d06d69c（地址）\nSystem.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5] ✅\n```\n\n## 7. 类型转换\n### 7.1 自动转换（小 → 大）\n```java\nbyte b = 10;\nint i = b;          // ✅ 自动转，没问题\ndouble d = i;       // ✅ 自动转，没问题\n```\n### 7.2 强制转换（大 → 小）\n```java\ndouble d = 3.99;\nint i = (int) d;    // ✅ 强制转，i = 3（小数直接丢掉！）\n\nint big = 130;\nbyte small = (byte) big;  // ⚠️ 溢出！结果是 -126\n```\n### 7.3 字符串与其他类型互转\n```java\n// 任意类型 → String\nString s1 = String.valueOf(123);    // \"123\"\nString s2 = 123 + \"\";               // \"123\"（偷懒写法）\n\n// String → 数字\nint num = Integer.parseInt(\"123\");           // 123\ndouble d = Double.parseDouble(\"3.14\");       // 3.14\n\n// 注意：格式不对会抛异常\nint error = Integer.parseInt(\"abc\");         // ❌ NumberFormatException\n```\n\n## 8. 常量与命名规范\n```java\n// 常量：final 修饰，全大写，单词间下划线分隔\nfinal double PI = 3.14159;\nfinal int MAX_SIZE = 100;\n\n// 变量命名：小驼峰\nint studentAge = 20;\nString userName = \"Tom\";\n\n// 类名：大驼峰（Main, StudentInfo）\n// 方法名：小驼峰（getName, printInfo）\n// 包名：全小写（com.example.demo）\n```",
            "url": "https://kyriechao.github.io/posts/2026/03/0/10-java-basics",
            "title": "Java 零基础入门：从 Hello World 到核心语法",
            "summary": "真正从零开始学 Java：从 System.out.println 讲起，逐步掌握。",
            "date_modified": "2026-03-09T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/0/08-hackers-and-painters",
            "content_html": "\n![Book Cover](https://images.unsplash.com/photo-1544716278-ca5e3f4abd8c?auto=format&fit=crop&q=80&w=1000)\n\n最近周末闲暇，我又拿起了保罗·格雷厄姆（Paul Graham）的经典文集《黑客与画家》。\n\n## 黑客与画家的共同点\n\n书中通过比较黑客（优秀的程序员）和画家，提出了一个有趣的观点：**黑客也是创造者**。\n\n> 黑客与画家的共同之处，在于他们都是创作者。与科学家不同，科学家的目的是发现真理，而黑客的目的是创造美好的事物。\n\n这一点我深有同感。编程不仅仅是逻辑的堆砌，更是结构与美学的构建。优雅的代码就像一幅构图精巧的画作，不仅功能完善，而且易于理解和维护。\n\n## 关于财富\n\nPG 在书中关于财富的定义也非常深刻：**财富不是金钱，财富是我们需要的东西**（食物、衣服、房子、汽车、假期等）。金钱只是交换财富的媒介。\n\n要创造财富，你需要做两件事：\n1.  **可测量性** (Measurement)：你的贡献可以被量化。\n2.  **可放大性** (Leverage)：你的工作成果可以被放大（例如通过软件服务千万人）。\n\n## 结语\n\n这本书不仅适合程序员阅读，也适合所有对创业、设计和思考感兴趣的人。它鼓励我们去创造、去打破常规、去追求卓越。\n\n如果你还没有读过，强烈推荐！\n",
            "url": "https://kyriechao.github.io/posts/2026/03/0/08-hackers-and-painters",
            "title": "重读《黑客与画家》有感",
            "summary": "保罗·格雷厄姆的《黑客与画家》是一本常读常新的经典。这次重读，我对“创造财富”和“设计品味”有了新的理解。",
            "date_modified": "2026-03-08T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/0/08-markdown-demo",
            "content_html": "\n欢迎阅读这篇功能展示文章！这里将为你演示如何使用 Markdown 撰写丰富的内容。\n\n## 文本样式\n\n你可以在文章中使用 **粗体**、*斜体*、~~删除线~~ 以及 `行内代码`。\n\n> 这是一个引用块。\n>\n> 支持多行引用。\n\n## 代码高亮\n\n支持多种语言的语法高亮：\n\n```python\ndef hello_world():\n    print(\"Hello, World!\")\n    return True\n\nif __name__ == \"__main__\":\n    hello_world()\n```\n\n```javascript\nconst greet = (name) => {\n  console.log(`Hello, ${name}!`);\n};\n\ngreet('Kyrie');\n```\n\n## 列表展示\n\n### 无序列表\n- 项目一\n- 项目二\n  - 子项目 A\n  - 子项目 B\n\n### 有序列表\n1. 第一步\n2. 第二步\n3. 第三步\n\n### 任务列表\n- [x] 已完成任务\n- [ ] 待办任务\n- [ ] 进行中...\n\n## 表格示例\n\n| 姓名 | 角色 | 技能 |\n|:-----|:-----:|-----:|\n| Kyrie | 开发者 | Ruby, Python |\n| User | 访客 | 阅读, 评论 |\n\n## 提示块 (Notices)\n\nNext.js 博客使用 HTML/Tailwind 替代了 Jekyll 的 Kramdown 语法：\n\n<div class=\"p-4 mb-4 text-blue-800 border-l-4 border-blue-300 bg-blue-50\">\n  <strong>Primary Notice</strong>\n  <p>这是一个主要提示块。</p>\n</div>\n\n<div class=\"p-4 mb-4 text-cyan-800 border-l-4 border-cyan-300 bg-cyan-50\">\n  <strong>Info Notice</strong>\n  <p>这是一个信息提示块。</p>\n</div>\n\n<div class=\"p-4 mb-4 text-yellow-800 border-l-4 border-yellow-300 bg-yellow-50\">\n  <strong>Warning Notice</strong>\n  <p>这是一个警告提示块。</p>\n</div>\n\n<div class=\"p-4 mb-4 text-red-800 border-l-4 border-red-300 bg-red-50\">\n  <strong>Danger Notice</strong>\n  <p>这是一个危险/错误提示块。</p>\n</div>\n\n## 按钮链接\n\n<div class=\"space-x-2\">\n  <a href=\"/\" class=\"inline-block px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 no-underline\">主要按钮</a>\n  <a href=\"/\" class=\"inline-block px-4 py-2 bg-green-600 text-white rounded hover:bg-green-700 no-underline\">成功按钮</a>\n  <a href=\"/\" class=\"inline-block px-4 py-2 bg-yellow-500 text-white rounded hover:bg-yellow-600 no-underline\">警告按钮</a>\n  <a href=\"/\" class=\"inline-block px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700 no-underline\">危险按钮</a>\n</div>\n\n## 图片与媒体\n\n你可以在文章中插入图片：\n\n![示例图片](https://source.unsplash.com/random/800x400/?technology)\n\n## 数学公式\n\n启用 MathJax 后，你可以编写 LaTeX 公式：\n\n行内公式：$E = mc^2$\n\n块级公式：\n\n$$\n\\sum_{i=1}^n i = \\frac{n(n+1)}{2}\n$$\n\n## 结语\n\n这就是迁移到 Next.js 后的一些核心功能展示。快去尝试写一篇属于你的文章吧！\n",
            "url": "https://kyriechao.github.io/posts/2026/03/0/08-markdown-demo",
            "title": "Markdown 功能展示与富文本示例",
            "summary": "这篇文章展示了 Minimal Mistakes 主题支持的各种 Markdown 语法，包括代码高亮、数学公式、提示块、表格与多媒体。",
            "date_modified": "2026-03-08T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/0/08-productivity-workflow",
            "content_html": "\n在这个信息爆炸的时代，建立一套适合自己的高效工作流至关重要。\n\n## 任务管理：Todoist\n\n我尝试过很多 To-Do List 应用，最终还是回归到了 **Todoist**。它的自然语言输入功能非常强大。\n\n*   **Inbox Zero**：每天早上清空收件箱，根据优先级安排当天的任务。\n*   **标签系统**：使用 `@工作`、`@生活`、`@学习` 等标签进行分类。\n\n## 笔记系统：Notion + Obsidian\n\n*   **Notion**：用于项目管理、数据库和协作文档。它的 Database 功能非常灵活，适合管理结构化数据。\n*   **Obsidian**：用于个人知识库（第二大脑）。基于 Markdown 的本地文件存储让我很有安全感，双向链接功能也非常适合建立知识网络。\n\n## 开发工具：Trae IDE\n\n最近我开始使用 **Trae IDE**，这是一款集成了 AI 能力的现代编辑器。\n\n> Trae 不仅仅是一个代码编辑器，它更像是一个智能编程助手。\n\n### 主要优势\n\n1.  **智能补全**：AI 能够根据上下文准确预测我想要写的代码。\n2.  **自然语言指令**：我可以直接告诉它“帮我创建一个 React 组件”，它就能自动生成代码。\n3.  **集成终端**：无需离开编辑器即可运行命令。\n\n## 结语\n\n工具只是辅助，核心还是在于坚持和执行。找到适合自己的工作流，并不断优化它，才能真正提高效率。\n",
            "url": "https://kyriechao.github.io/posts/2026/03/0/08-productivity-workflow",
            "title": "我的 2026 高效工作流分享",
            "summary": "工欲善其事，必先利其器。本文将介绍我在 2026 年使用的一套高效工作流，涵盖任务管理、笔记记录和代码开发。",
            "date_modified": "2026-03-08T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        },
        {
            "id": "https://kyriechao.github.io/posts/2026/03/0/08-tailwind-tips",
            "content_html": "\n## 为什么选择 Tailwind CSS？\n\nTailwind CSS 是一个功能类优先（utility-first）的 CSS 框架。与 Bootstrap 等传统框架不同，它不提供预制的组件，而是提供了一套底层的工具类，让你能够直接在 HTML 中构建完全定制的设计。\n\n### 1. 使用 `@apply` 抽取组件\n\n虽然 Tailwind 提倡在 HTML 中写样式，但在某些情况下（比如按钮、输入框），如果多个地方用到相同的样式组合，可以使用 `@apply` 指令来抽取成一个 CSS 类。\n\n```css\n/* globals.css */\n@layer components {\n  .btn-primary {\n    @apply px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors;\n  }\n}\n```\n\n### 2. 自定义配置 `tailwind.config.ts`\n\nTailwind 的强大之处在于其可配置性。你可以轻松地扩展默认的主题，添加自定义的颜色、字体或断点。\n\n```typescript\n// tailwind.config.ts\nimport type { Config } from \"tailwindcss\";\n\nconst config: Config = {\n  theme: {\n    extend: {\n      colors: {\n        'brand-blue': '#0070f3',\n      },\n      spacing: {\n        '128': '32rem',\n      }\n    },\n  },\n  // ...\n};\nexport default config;\n```\n\n### 3. 暗黑模式 (Dark Mode)\n\nTailwind 内置了对暗黑模式的支持。只需在类名前加上 `dark:` 前缀即可。\n\n```html\n<div class=\"bg-white dark:bg-gray-800 text-black dark:text-white\">\n  <h1>自动适配暗黑模式的标题</h1>\n</div>\n```\n\n## 总结\n\nTailwind CSS 极大地提高了前端开发的效率，一旦习惯了这种写法，你就很难回去了。希望这些小技巧对你有帮助！\n",
            "url": "https://kyriechao.github.io/posts/2026/03/0/08-tailwind-tips",
            "title": "Tailwind CSS 实用技巧分享",
            "summary": "在这篇文章中，我将分享一些我在使用 Tailwind CSS 开发项目时总结的实用技巧，帮助你更高效地构建现代网页界面。",
            "date_modified": "2026-03-08T00:00:00.000Z",
            "author": {
                "name": "Kyrie Chao",
                "url": "https://github.com/KyrieChao"
            }
        }
    ]
}