[{"data":1,"prerenderedAt":323},["ShallowReactive",2],{"home":3},{"title":4,"articles":5,"hot_article":108,"month_arch":171,"total":319,"cover":320},"首页 - 独孤影的博客",[6,18,28,39,49,58,68,79,89,99],{"id":7,"title":8,"uri":9,"author":10,"tags":11,"created_at":15,"view_count":16,"content":17},127,"普通人自建基于 WiFi Calling 的通讯网络能否行得通","is-it-feasible-for-an-ordinary-person-to-build-their-own-communication-network-based-on-Wi-Fi-Calling","duguying",[12,13,14],"WiFi-Calling","语音通话","通讯","2026-07-28 18:13:28",29,"\u003Cblockquote>\n\u003Cp>写于：2026 年中\n一句话结论：\u003Cstrong>不行。\u003C\u002Fstrong> 普通人没有合法、可行的通道走&rdquo;运营商级 WiFi Calling&rdquo;。技术上被三重锁死，\u003Cstrong>第一道且最硬的一道就是 eUICC 证书信任链——私有证书行不通，这是硅片级的死结\u003C\u002Fstrong>。能做的最远边界是\u003Cstrong>应用层 VoIP over WiFi\u003C\u002Fstrong>，\u003Cstrong>那不是 WiFi Calling\u003C\u002Fstrong>。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Chr \u002F>\n\n\u003Ch2>0. 写这篇文章的动机\u003C\u002Fh2>\n\n\u003Cp>我最近研究esim，在尝试做一个 SGP.22 SM-DP+ 的开源实现，仓库叫 \u003Ccode class=\"inline-code\">sm-dp\u003C\u002Fcode>，目标是把&rdquo;用户从二维码触发 eSIM Profile 下载&rdquo;这条链路完整跑通。几个月时间，我把 ES9+ 第一步 \u003Ccode class=\"inline-code\">initiateAuthentication\u003C\u002Fcode> 写完、容器化、跑通 HTTPS、跑通 nginx 反代、修了一遍又一遍的协议头（大小写不敏感 + 多版本协议号支持），最后 MiniLPA 真的把请求发到我服务器上，\u003Cstrong>sm-dp 返回 HTTP 200 + 标准失败 envelope\u003C\u002Fstrong>，错误码 \u003Ccode class=\"inline-code\">subjectCode 8.8.4 \u002F reasonCode 3.7\u003C\u002Fcode>，信息是：\u003C\u002Fp>\n\n\u003Cblockquote>\n\u003Cp>&ldquo;The SM-DP+ has no CERT.DPauth.ECDSA signed by a CI Public Key supported by the eUICC&rdquo;\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\n\u003Cp>这一行错误信息不是 bug，不是协议实现不对，不是网络问题。它\u003Cstrong>就是答案\u003C\u002Fstrong>：eUICC 里没有能信任我 SM-DP+ 证书的 CI 公钥。\u003C\u002Fp>\n\n\u003Cp>撞墙的位置：\u003Cstrong>eUICC 证书信任链\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Cp>我把这条墙讲清楚，再回答&rdquo;那能不能自建 WiFi Calling&rdquo;这个问题。\u003C\u002Fp>\n\n\u003Chr \u002F>\n\n\u003Ch2>1. 先把两件事分清楚：运营商 WiFi Calling ≠ 应用层 VoIP over WiFi\u003C\u002Fh2>\n\n\u003Cp>读者里肯定有人把这两个当回事，所以我必须先拆开：\u003C\u002Fp>\n\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>\u003C\u002Fth>\n\u003Cth>运营商 WiFi Calling（VoWiFi）\u003C\u002Fth>\n\u003Cth>应用层 VoIP over WiFi\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>信令\u003C\u002Ftd>\n\u003Ctd>IMS SIP（P-CSCF \u002F S-CSCF \u002F I-CSCF \u002F TAS）\u003C\u002Ftd>\n\u003Ctd>App 自有协议（APNs \u002F Signal Protocol \u002F 自建 WS）\u003C\u002Ftd>\n\u003C\u002Ftr>\n\n\u003Ctr>\n\u003Ctd>鉴权凭据\u003C\u002Ftd>\n\u003Ctd>\u003Cstrong>ISIM @ UICC\u003C\u002Fstrong>（SIM 卡里那个 SIP Digest-AKA 凭据）\u003C\u002Ftd>\n\u003Ctd>App 账户 \u002F 设备证书\u003C\u002Ftd>\n\u003C\u002Ftr>\n\n\u003Ctr>\n\u003Ctd>寻址\u003C\u002Ftd>\n\u003Ctd>E.164 MSISDN（手机号）\u003C\u002Ftd>\n\u003Ctd>App ID \u002F UUID \u002F 自编号\u003C\u002Ftd>\n\u003C\u002Ftr>\n\n\u003Ctr>\n\u003Ctd>接入网\u003C\u002Ftd>\n\u003Ctd>不可信 WiFi + \u003Cstrong>IPsec\u002FIKEv2 → ePDG → EPC\u002F5GC → IMS\u003C\u002Fstrong>\u003C\u002Ftd>\n\u003Ctd>任何 IP 承载，裸 socket\u003C\u002Ftd>\n\u003C\u002Ftr>\n\n\u003Ctr>\n\u003Ctd>通话对象\u003C\u002Ftd>\n\u003Ctd>跨运营商经 IPX \u002F IBCF 进 PSTN\u003C\u002Ftd>\n\u003Ctd>私域；想进 PSTN 需 SIP Trunk\u003C\u002Ftd>\n\u003C\u002Ftr>\n\n\u003Ctr>\n\u003Ctd>紧急呼叫\u003C\u002Ftd>\n\u003Ctd>E.112 \u002F E.911 路径（受 PLMN 管制）\u003C\u002Ftd>\n\u003Ctd>通常无合规紧急呼叫能力\u003C\u002Ftd>\n\u003C\u002Ftr>\n\n\u003Ctr>\n\u003Ctd>计费 \u002F 合法监听\u003C\u002Ftd>\n\u003Ctd>运营商 CDR + LI 合规\u003C\u002Ftd>\n\u003Ctd>App 厂商服务器侧，不在运营商 CDR\u003C\u002Ftd>\n\u003C\u002Ftr>\n\n\u003Ctr>\n\u003Ctd>在手机拨号盘里\u003C\u002Ftd>\n\u003Ctd>是，作为运营商主叫\u003C\u002Ftd>\n\u003Ctd>不是，仅 App 自己的来电界面\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Cp>运营商\u003Cstrong>WiFi Calling \u002F VoWiFi\u003C\u002Fstrong> 是一个 3GPP 规范的术语，本质是&rdquo;把 IMS 语音搬到不可信 WiFi 上&rdquo;。它不是&rdquo;用 WiFi 打电话的 App&rdquo;。\u003Cstrong>两者在协议层完全平行，互不相通\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Cp>读者能做的最远边界——也是我自己正在做的——是右列那一条。\u003Cstrong>那不是 WiFi Calling\u003C\u002Fstrong>。这一点必须先说死，不然下面没法讲。\u003C\u002Fp>\n\n\u003Chr \u002F>\n\n\u003Ch2>2. 第一道锁（也是最硬的一道）：eUICC 证书信任链无法自建，私有证书就是不行\u003C\u002Fh2>\n\n\u003Cp>这一节是整篇文章最重要的一节，我会写得很慢。\u003C\u002Fp>\n\n\u003Ch3>2.1 我手里这块 eUICC 里到底有什么\u003C\u002Fh3>\n\n\u003Cp>我用的是一块 Eastcompeace（东信和平）生产、GSMA SAS 编号 \u003Ccode class=\"inline-code\">ED-ZI-UP-0826\u003C\u002Fcode> 的消费级商用 eUICC。svn 是 2.2.2，euiccFirmwareVer 4.2.0。我用 MiniLPA 读出 \u003Ccode class=\"inline-code\">EUICCInfo2\u003C\u002Fcode>，展开 \u003Ccode class=\"inline-code\">euiccCiPKIdListForVerification\u003C\u002Fcode>：\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>euiccCiPKIdListForVerification: [\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>  &#34;81370f5125d0b1d408d4c3b232e6d25e795bebfb&#34;,  \u002F\u002F GSM Association - RSP2 Root CI1\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>euiccCiPKIdListForSigning: [\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>  &#34;81370f5125d0b1d408d4c3b232e6d25e795bebfb&#34;,  \u002F\u002F GSM Association - RSP2 Root CI1\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>\u003Cstrong>就一项\u003C\u002Fstrong>。不是&rdquo;列表里主要是 GSMA，但也可以有别人&rdquo;——是\u003Cstrong>只有一项\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Cp>这一项是 \u003Ccode class=\"inline-code\">GSM Association - RSP2 Root CI1\u003C\u002Fcode> 的公钥指纹。整张芯片一辈子就认这一个 CI。它的公钥在 eUICC 出厂时被\u003Cstrong>一次性烧入 eUICC 内部的 ECASD（Embedded Certificate Authority Security Domain）\u003C\u002Fstrong>—— ECASD 是 eUICC 的 JavaCard 安全元件里专门管理证书链的应用。这个烧入是 OTP（一次性可编程）或同等强度的写保护，软件层面改不了。\u003C\u002Fp>\n\n\u003Ch3>2.2 我试过&rdquo;绕过&rdquo;，结果就是不行\u003C\u002Fh3>\n\n\u003Cp>我 OpenSSL 生成了一对 ECDSA P-256 私钥，自签了一张 CI 证书，role OID 用 \u003Ccode class=\"inline-code\">id-rsp-ci\u003C\u002Fcode>（\u003Ccode class=\"inline-code\">1.3.6.1.4.1.46898.3\u003C\u002Fcode>）。再用这张自签 CI 给 SM-DP+ 签了一张 \u003Ccode class=\"inline-code\">CERT.DPauth.ECDSA\u003C\u002Fcode>，role OID 用 \u003Ccode class=\"inline-code\">id-rsp-dp-authentication\u003C\u002Fcode>。我把 SM-DP+ 跑起来，让 MiniLPA 触发下载。\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>协议层面一切正常\u003C\u002Fstrong>。\u003Ccode class=\"inline-code\">POST \u002Fgsma\u002Frsp2\u002Fes9plus\u002FinitiateAuthentication\u003C\u002Fcode> 200 OK，所有握手都过，serverCertificate 也下发了。但\u003Cstrong>真正的 Profile 下载在 ES10b 阶段被 eUICC 拒接\u003C\u002Fstrong>——eUICC 拿到 \u003Ccode class=\"inline-code\">serverCertificate\u003C\u002Fcode> 后会沿着 \u003Ccode class=\"inline-code\">DPauth → CI → GSMA Root CI1\u003C\u002Fcode> 做链式验签，发现&rdquo;这张 DPauth 是由一张\u003Cstrong>不在我信任集\u003C\u002Fstrong>的 CI 签的&rdquo;，链路到我的自签 CI 时断了，回退到根时对不上，于是拒接。\u003C\u002Fp>\n\n\u003Cp>报错就是上面那个 \u003Ccode class=\"inline-code\">8.8.4 \u002F 3.7\u003C\u002Fcode>：&rdquo;无 CI 信任的 ECDSA 证书&rdquo;。\u003C\u002Fp>\n\n\u003Ch3>2.3 一个常见误区：&rdquo;装个根证书到手机能不能绕过&rdquo;\u003C\u002Fh3>\n\n\u003Cp>有人问过我：&rdquo;那我把 GSMA Root CI1 的证书装到手机的系统信任根里，不就解决了吗？&rdquo;\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>不行\u003C\u002Fstrong>。这是一个典型的认知误区，原因有两个：\u003C\u002Fp>\n\n\u003Cp>第一，\u003Cstrong>iOS \u002F Android 的&rdquo;系统信任根&rdquo;只管 TLS \u002F HTTPS 客户端校验\u003C\u002Fstrong>，跟 eUICC 完全无关。iOS 上你装一个企业 CA 根证书，它影响的是 Safari \u002F Mail \u002F 第三方 App 怎么验 HTTPS 服务器。Android 7+ 默认连用户 CA 都不信，要 root 才能让 App 默认信用户 CA。但 eUICC 是\u003Cstrong>独立的 JavaCard 安全元件\u003C\u002Fstrong>，跟主系统隔离，主系统的&rdquo;信任根&rdquo;管不到它。eUICC 验签用的是它自己烧死在 ECASD 里的那张 GSMA Root CI1 公钥，\u003Cstrong>只此一份\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Cp>第二，\u003Cstrong>eUICC 的 ECASD 没有外部 APDU 写接口\u003C\u002Fstrong>。ECASD 是 eUICC OS 厂商（Eastcompeace \u002F Idemia \u002F Thales 等）在工厂模式下用厂商签名固件装入的，运行态下不允许任何 APDU 修改它的信任列表。你能换的只有 Profile（普通应用），碰不到 ECASD。能&rdquo;修改&rdquo; ECASD 信任列表的唯一合法途径是 eUICC 固件更新——但固件更新包也得由 EUM（eUICC 厂商）私钥签发，EUM 的根又是 GSMA Root CI。\u003Cstrong>走到哪里都绕回 GSMA\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Ch3>2.4 那&rdquo;开发 \u002F 仿真 eUICC&rdquo;呢？—— 能绕，但用途有限\u003C\u002Fh3>\n\n\u003Cp>必须诚实补一刀：\u003Cstrong>&ldquo;绕&rdquo;对部分 eUICC 是可以的\u003C\u002Fstrong>。少数开发 \u002F 仿真 eUICC 的 ECASD 是&rdquo;软&rdquo;的，工厂模式允许加载额外 CI 信任锚：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>osmocom 系列（\u003Ccode class=\"inline-code\">swp-euicc\u003C\u002Fcode> 等）\u003C\u002Fli>\n\u003Cli>某些 Idemia \u002F Valid 测试卡（需通过厂商渠道申请，明确&rdquo;测试模式&rdquo;）\u003C\u002Fli>\n\u003Cli>纯软件仿真器（如某些 JavaCard 仿真器）\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>在这些卡上，你可以把自己生成的自签 CI 公钥塞进信任集，\u003Cstrong>eUICC 就会信\u003C\u002Fstrong>。但这些卡不是消费级 eUICC，\u003Cstrong>不能用于任何商用场景\u003C\u002Fstrong>（运营商商用网不识别它们）。它们的用途是：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>协议层验证（&rdquo;我的 SM-DP+ 协议实现对不对&rdquo;）✓\u003C\u002Fli>\n\u003Cli>端到端加密路径验证（&rdquo;签名验签链路通不通&rdquo;）✓\u003C\u002Fli>\n\u003Cli>真实商用 Profile 下载 ✗\u003C\u002Fli>\n\u003Cli>让消费级手机原生电话 App 把你的 SM-DP+ 当运营商 ✗\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>\u003Cstrong>我手头这块 Eastcompeace 不在列\u003C\u002Fstrong>。本文后面所有讨论都以&rdquo;商用 eUICC&rdquo;为前提。开发卡的&rdquo;绕过&rdquo;是另一篇文章的话题，不能混淆。\u003C\u002Fp>\n\n\u003Ch3>2.5 GSMA PKI 的真相\u003C\u002Fh3>\n\n\u003Cp>把上面这些事实落到 GSMA PKI 体系里看：\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>GSMA Root CI（公开信任锚，固化在每张合规 eUICC 的 ECASD）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    │ 由 GSMA Root CI 私钥签发\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    ▼\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>Sub-CI（也叫 CI）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    │ - EUM 厂商（Eastcompeace \u002F Idemia \u002F Thales \u002F Valid \u002F G+D 等）持有\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    │ - 这些 CI 的公钥被它们出厂的 eUICC 信任\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    ▼\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>End-entity 证书：SM-DP+ \u002F SM-DS \u002F EUM\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>这张图的三个特征决定了&rdquo;个人不可能&rdquo;：\u003C\u002Fp>\n\n\u003Col>\n\u003Cli>\u003Cp>\u003Cstrong>Root CI 私钥不卖\u003C\u002Fstrong>：GSMA Root CI 私钥物理隔离在 GSMA 金库里，没有任何合法出售渠道。任何说&rdquo;出售 GSMA Root CI 私钥&rdquo;的网站都是骗子。\u003C\u002Fp>\u003C\u002Fli>\n\n\u003Cli>\u003Cp>\u003Cstrong>Sub-CI 只发给通过 SAS-SM \u002F SAS-SDS 现场审计的企业\u003C\u002Fstrong>：SAS 是 GSMA 的安全认证体系，\u003Cstrong>SM（Subscription Management）\u003C\u002Fstrong>针对 SM-DP+ 服务，\u003Cstrong>SDS\u003C\u002Fstrong> 针对 SM-DS。审计员是 GSMA 授权的第三方（BSI \u002F atsec \u002F TÜV 等），要查你的：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>HSM（硬件安全模块，FIPS 140-2 Level 3 或同等），\u003Cstrong>必须\u003C\u002Fstrong>，不能把 SM-DP+ 私钥存成 PEM 文件塞进容器\u003C\u002Fli>\n\u003Cli>物理安保（双人控制、门禁、监控）\u003C\u002Fli>\n\u003Cli>密钥管理与备份方案\u003C\u002Fli>\n\u003Cli>站点隔离、网络隔离\u003C\u002Fli>\n\u003Cli>人员背景调查\u003C\u002Fli>\n\u003Cli>年度复审\u003C\u002Fli>\n\u003C\u002Ful>\u003C\u002Fli>\n\n\u003Cli>\u003Cp>\u003Cstrong>审计周期和钱\u003C\u002Fstrong>：从启动到拿证 \u003Cstrong>6 到 12 个月\u003C\u002Fstrong>，钱 \u003Cstrong>六位数 USD 起步\u003C\u002Fstrong>（HSM 几万到十几万 + 审计费 + 持续合规运维人力）。\u003C\u002Fp>\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>任何个人 \u002F 小团队，\u003Cstrong>没有这条路\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Ch3>2.6 结论（这一节最重要的一句话）\u003C\u002Fh3>\n\n\u003Cp>\u003Cstrong>eUICC 证书信任链是个人根本无法触碰的硅片级约束，私有证书行不通，这不是软件问题。\u003C\u002Fstrong>\u003C\u002Fp>\n\n\u003Cp>我在 \u003Ccode class=\"inline-code\">sm-dp\u003C\u002Fcode> 里跑通 ES9+ 协议层之后撞上的墙，就是这堵墙。\u003Cstrong>这堵墙在我手里这块 Eastcompeace 芯片上不可逾越\u003C\u002Fstrong>，对任何商用 eUICC 都不可逾越，对所有 GSMA 体系内的&rdquo;自建 SM-DP+&ldquo;尝试都不可逾越。这是 eSIM 安全模型的核心：如果信任锚能由软件改写，整个体系就崩了。\u003C\u002Fp>\n\n\u003Chr \u002F>\n\n\u003Ch2>3. 第二道锁：运营商 IMS 核心网不是你想接就能接\u003C\u002Fh2>\n\n\u003Cp>即便你解决了证书链（你做不到，但假设做到了），下一个墙是\u003Cstrong>运营商 IMS 核心网\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Cp>运营商 WiFi Calling 的完整链路是：\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>UE（手机）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   │  IPsec\u002FIKEv2 隧道（SWu 接口，TS 33.402）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   ▼\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>ePDG（运营商侧加密网关，Cisco \u002F Ericsson \u002F Nokia \u002F Mavenir 商用产品）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   │  GTP \u002F PMIPv6（3GPP TS 23.402 S2b \u002F TS 23.501 非授信接入）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   ▼\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>EPC \u002F 5GC（核心网：MME \u002F SGW \u002F PGW \u002F AMF \u002F SMF \u002F UPF）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   │  SIP（IMS 接入）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   ▼\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>IMS 核心：P-CSCF → I-CSCF → S-CSCF → TAS → SBC\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   │                                    │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   │                                    │ 媒体：RTP \u002F SRTP（受 QoS 控制）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   ▼                                    ▼\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>HSS \u002F UDR（用户数据库，存 ISIM 凭据）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   │ 跨运营商经 IBCF 进 IPX → 对端运营商 IMS → PSTN\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>相关 3GPP 规范锚点：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>TS 23.228（IMS 总体）\u003C\u002Fli>\n\u003Cli>TS 23.402（非授信非 3GPP 接入，ePDG \u002F SWu \u002F S2b）\u003C\u002Fli>\n\u003Cli>TS 23.501 \u002F 23.502（5G 系统架构，非授信接入归入 5GC）\u003C\u002Fli>\n\u003Cli>TS 24.502（非授信接入的会话管理）\u003C\u002Fli>\n\u003Cli>TS 33.402（IKEv2 \u002F IPsec 安全）\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>IMS 凭据在 \u003Cstrong>ISIM @ UICC\u003C\u002Fstrong>——也就是 SIM 卡里的 SIP Digest-AKA 凭据。\u003Cstrong>这意味着&rdquo;号码&rdquo;绑在 SIM 卡上，不绑在 App 上\u003C\u002Fstrong>。你给朋友发个&rdquo;我的 WiFi Calling 号码是 13800138000&rdquo;，那个号码必须在运营商 HSS 里登记，必须有 ISIM 凭据，必须有对应的 SM-DP+ Profile 已下发到某张 UICC。这条链路\u003Cstrong>没有任何一个环节是开放的\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>ePDG 是运营商专有设备\u003C\u002Fli>\n\u003Cli>HSS\u002FHLR 是运营商核心数据库\u003C\u002Fli>\n\u003Cli>IBCF 是运营商间边界\u003C\u002Fli>\n\u003Cli>IPX 是 GSMA 运营的 IP 交换网络（Equinix \u002F Etisalat \u002F Tata 是主要运营商），\u003Cstrong>不接入散户\u003C\u002Fstrong>\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>这是企业基础设施级别的封闭，普通人连&rdquo;租一个 IMS&rdquo;都做不到。\u003C\u002Fp>\n\n\u003Chr \u002F>\n\n\u003Ch2>4. 第三道锁：iOS \u002F Android WiFi Calling 入口是运营商 entitlement，不是用户开关\u003C\u002Fh2>\n\n\u003Cp>假设（不可能的假设）你前两关都过了，你的 SM-DP+ 把 Profile 下发到了某张 eUICC 上，你还需要 iOS \u002F Android 在系统设置里\u003Cstrong>亮出&rdquo;WiFi 通话&rdquo;开关\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Ch3>4.1 iOS\u003C\u002Fh3>\n\n\u003Cp>iOS 的 WiFi Calling 开关不是用户能强制打开的。它出现的条件是：\u003C\u002Fp>\n\n\u003Col>\n\u003Cli>系统读到一张\u003Cstrong>经过 Apple 审核的 carrier bundle \u002F IPCC\u003C\u002Fstrong>（carrier.plist + 配套 APN、IMS entitlement、号码映射）\u003C\u002Fli>\n\u003Cli>Modem 固件支持 IMS VoWiFi\u003C\u002Fli>\n\u003Cli>eUICC 上有对应运营商下发的 Profile\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>参考 Apple 官方文档：\u003Ca href=\"https:\u002F\u002Fsupport.apple.com\u002Fen-us\u002F108066\">Use Wi-Fi calling on your iPhone\u003C\u002Fa>。Apple 维护一个运营商审核流程，运营商必须提交自己的 carrier bundle 经 Apple 审核。\u003Cstrong>散户的 carrier bundle 不会被审核\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Ch3>4.2 Android\u003C\u002Fh3>\n\n\u003Cp>Android 类似但更复杂。Android 11+ 的 IMS API（\u003Ccode class=\"inline-code\">ImsService\u003C\u002Fcode> \u002F \u003Ccode class=\"inline-code\">ImsManager\u003C\u002Fcode>）是 \u003Cstrong>AOSP 提供的接口框架\u003C\u002Fstrong>，但\u003Cstrong>实现必须由运营商提供\u003C\u002Fstrong>：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>运营商的 IMS 服务二进制（OEM \u002F modem 厂商预装或系统镜像里）\u003C\u002Fli>\n\u003Cli>\u003Ccode class=\"inline-code\">carrier_config.xml\u003C\u002Fcode> \u002F carrier_settings 配置文件（描述 PLMN、APN、IMS entitlement）\u003C\u002Fli>\n\u003Cli>Modem HAL（QMI \u002F RIL \u002F IMS 调制解调器层）支持 IMS\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>参考 Android 官方文档：\u003Ca href=\"https:\u002F\u002Fsource.android.com\u002Fdocs\u002Fcore\u002Fconnect\u002Fwifi-wifi-calling\">Wi-Fi calling\u003C\u002Fa>。\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>裸 AOSP \u002F 第三方 ROM 没有这些运营商二进制，永远显示不出 WiFi Calling 开关\u003C\u002Fstrong>。LineageOS \u002F GrapheneOS \u002F Pixel Experience 上你都不会看到这个开关。\u003C\u002Fp>\n\n\u003Ch3>4.3 平台权力\u003C\u002Fh3>\n\n\u003Cp>这一道锁的本质是\u003Cstrong>平台权力\u003C\u002Fstrong>——苹果和 Google 决定&rdquo;哪个运营商能被点亮&rdquo;。这不是开放接口，不是 SDK 调一调就能开。运营商必须：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>走 Apple 的 Carrier Bundle 审核流程\u003C\u002Fli>\n\u003Cli>与 Google 签 carrier entitlement 协议\u003C\u002Fli>\n\u003Cli>接受苹果 \u002F 谷歌的合规审计\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>普通开发者、创业公司想绕过去？没门。\u003C\u002Fp>\n\n\u003Chr \u002F>\n\n\u003Ch2>5. 应用层 VoIP 进不去 IMS\u003C\u002Fh2>\n\n\u003Cp>行，我们退一万步——\u003Cstrong>不追求运营商 WiFi Calling\u003C\u002Fstrong>，就做个 App 让你和朋友能语音通话。是不是就行了？\u003C\u002Fp>\n\n\u003Cp>可以。但要清醒地知道，这条路是\u003Cstrong>和 IMS 完全平行\u003C\u002Fstrong>的另一条路：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>\u003Cstrong>FaceTime\u003C\u002Fstrong>：信令走 APNs，媒体 P2P（ICE\u002FSTUN\u002FTURN），完全不进运营商 IMS \u002F ePDG \u002F SIP \u002F ISIM\u003C\u002Fli>\n\u003Cli>\u003Cstrong>WhatsApp \u002F Signal \u002F Telegram\u003C\u002Fstrong>：应用层 E2EE（Signal Protocol 等），UDP\u002FTCP\u002FTLS 上的 proprietary 信令，不注册到 IMS，不用 ePDG，不读 ISIM\u003C\u002Fli>\n\u003Cli>\u003Cstrong>我的 \u003Ccode class=\"inline-code\">rtpd\u003C\u002Fcode>\u003C\u002Fstrong>：自建 WebSocket 信令 + UDP RTP 媒体，\u003Ccode class=\"inline-code\">phone-android\u003C\u002Fcode> \u002F \u003Ccode class=\"inline-code\">phone-ios\u003C\u002Fcode> 通过 UniFFI 调用 \u003Ccode class=\"inline-code\">rtpd-sdk\u003C\u002Fcode>，跑的是 48 kHz Opus \u002F G.711 PCMU over UDP RTP\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>这些应用在 \u003Cstrong>App 层面\u003C\u002Fstrong> 用 CallKit \u002F VOIP Push 让锁屏体验接近原生电话——iOS 上 App 可以在锁屏弹出来电 UI，点击接听，CallKit 让通话显示在系统的&rdquo;通话中&rdquo;界面里。\u003Cstrong>但媒体始终在 App 自己控制的实时连接里\u003C\u002Fstrong>，不进运营商核心网，不被运营商 CDR 计费，不走 ePDG，不使用 ISIM 凭据。\u003C\u002Fp>\n\n\u003Cp>GSMA 在 2018 年的一份白皮书里把这类服务统称为 \u003Cstrong>OTT（Over-The-Top）VoIP\u003C\u002Fstrong>，明确区别于运营商 VoWiFi：\u003Ca href=\"https:\u002F\u002Fwww.gsma.com\u002Fnewsroom\u002Fblog\u002Fott-calling-protocol-analysis\u002F\">GSMA blog - OTT Calling Protocol Analysis\u003C\u002Fa>。\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>互通 PSTN 仍然需要 SIP Trunk + 落地号\u003C\u002Fstrong>，回到运营商身份，绕不开。\u003C\u002Fp>\n\n\u003Chr \u002F>\n\n\u003Ch2>6. GSMA SGP.22 &ldquo;999&rdquo; 是不是个人通道？\u003C\u002Fh2>\n\n\u003Cp>不是。这是个常见误解。\u003C\u002Fp>\n\n\u003Cp>GSMA 在 SGP.22 体系里确实给\u003Cstrong>非传统运营商 \u002F 私有网络 \u002F MVNO\u003C\u002Fstrong> 保留了一个\u003Cstrong>简化通道\u003C\u002Fstrong>，对应 Apple iOS 上的&rdquo;999 标识&rdquo;。它的本质是：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>\u003Cstrong>简化版 SAS-SM 合规\u003C\u002Fstrong>（不是免除，仍然要 GSMA 注册 + 当地监管备案）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>iOS PLMN 白名单\u003C\u002Fstrong>（&rdquo;999&rdquo;是一个 MCC\u002FMNC 范围，由 Apple 审核）\u003C\u002Fli>\n\u003Cli>\u003Cstrong>走标准 SGP.22 协议\u003C\u002Fstrong>（仍然是 GSMA Root CI 签发的 CI 链）\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>它的目标客户是：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>企业 \u002F 园区 \u002F 港口 \u002F 矿区 \u002F 校园的\u003Cstrong>私有 5G\u002FLTE 网络\u003C\u002Fstrong>运营商\u003C\u002Fli>\n\u003Cli>有 ICP\u002FISP 资质的 MVNO\u003C\u002Fli>\n\u003Cli>政府 \u002F 应急 \u002F 国防通信\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>不是个人。仍然要法人主体，仍然要 GSMA 注册，仍然要合规审计。它\u003Cstrong>降的是合规门槛，不是消除 GSMA PKI\u003C\u002Fstrong>——这点要讲清楚。\u003C\u002Fp>\n\n\u003Chr \u002F>\n\n\u003Ch2>7. &ldquo;那我做私有 IMS 网不就完了？&rdquo;——是的，企业可行，个人不是合适主体\u003C\u002Fh2>\n\n\u003Cp>有些读者看到上面可能会想：&rdquo;既然问题在 IMS 核心网，那我自建一个 IMS 私有网给园区用不就行了？&rdquo;\u003C\u002Fp>\n\n\u003Cp>这条路\u003Cstrong>技术上完全可行\u003C\u002Fstrong>，且已经有很多商用产品。具体拓扑：\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>私有基站（small cell \u002F 5G gNB）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    ▼\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>私有 5GC：AMF \u002F SMF \u002F UPF\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    ▼\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>私有 IMS：CSCF \u002F TAS \u002F SBC \u002F HSS\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    ▼\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>私有 ISIM \u002F USIM 凭据 → 行业终端（防爆手机 \u002F 三防 PDA \u002F 工业模组）\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>商用方案提供商：Athonet、Celona、CoreNetwork Dynamics、Mavenir、华为、中兴、京信、佰才邦等。\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>能做什么\u003C\u002Fstrong>：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>✅ 园区内部打通：工人 A 给工人 B 走 IMS VoNR \u002F VoLTE\u003C\u002Fli>\n\u003Cli>✅ 行业终端之间互联互通\u003C\u002Fli>\n\u003Cli>✅ 私有网络的 QoS \u002F 紧急呼叫 \u002F 计费 \u002F 调度\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>\u003Cstrong>不能做什么\u003C\u002Fstrong>：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>✗ 出园区打电话给北京\u002F纽约\u002F伦敦的朋友：仍要公网运营商 SIP Trunk + 落地号\u003C\u002Fli>\n\u003Cli>✗ 让消费级 iPhone \u002F 普通 Android 原生电话 App 显示为你的私有运营商：需要 Apple \u002F Google 审核 carrier bundle，要 GSMA PKI 颁发合法 SM-DP+ 证书链\u003C\u002Fli>\n\u003Cli>✗ 在淘宝上卖给个人用户：合规不允许\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>\u003Cstrong>个人适不适合\u003C\u002Fstrong>：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>50–500 用户的\u003Cstrong>企业\u003C\u002Fstrong>场景：可走 Athonet \u002F Celona \u002F Mavenir 这类 SaaS 化方案，半年到一年部署完成，百万级人民币预算\u003C\u002Fli>\n\u003Cli>\u003Cstrong>个人 \u002F 小作坊\u003C\u002Fstrong>：不合适。HSM + 现场审计 + GSMA 注册 + 当地工信部备案（频谱 + 电信业务资质），百万级设备和合规起步\u003C\u002Fli>\n\u003Cli>\u003Cstrong>不要混淆\u003C\u002Fstrong>：私有 IMS 网不是&rdquo;WiFi Calling 替代品&rdquo;，它是\u003Cstrong>企业自建专网方案\u003C\u002Fstrong>\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>\u003Cstrong>结论\u003C\u002Fstrong>：私有 IMS 是\u003Cstrong>企业选项\u003C\u002Fstrong>，不是个人 &ldquo;WiFi Calling&rdquo; 的替代品。\u003C\u002Fp>\n\n\u003Chr \u002F>\n\n\u003Ch2>8. 我手头这套栈在整张图里是什么位置\u003C\u002Fh2>\n\n\u003Cp>让我把我自己跑的东西摆出来，给读者一个具象的&rdquo;普通人能做的最远边界&rdquo;：\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>┌──────────────────────────────────────────────────────────────┐\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│ phone-android (Kotlin \u002F Compose, 48 kHz Opus over UDP RTP)   │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│ phone-ios     (SwiftUI, G.711 PCMU over UDP RTP)            │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│       │                                                        │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│       │ UniFFI bindings                                        │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│       ▼                                                        │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│ rtpd-sdk (Rust)                                                │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│       │                                                        │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│       │ WebSocket signaling + UDP RTP                         │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│       ▼                                                        │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│ rtpd (Rust) ──── PostgreSQL（短号注册、状态）                  │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>└──────────────────────────────────────────────────────────────┘\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>        完全在 IP 层之上，不进运营商 IMS \u002F ePDG \u002F ISIM\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>        类比：FaceTime \u002F WhatsApp \u002F Signal \u002F 自建 SIP PBX\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>┌──────────────────────────────────────────────────────────────┐\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│ MiniLPA (Kotlin 桌面 GUI)                                      │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│       │                                                        │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│       │ GSMA RSP 流程                                          │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│       ▼                                                        │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│ sm-dp (Rust \u002F Rocket, SGP.22 SM-DP+)                         │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│   - ES9+ \u002Fgsma\u002Frsp2\u002Fes9plus\u002FinitiateAuthentication ✓          │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│   - ES9+ \u002Fgsma\u002Frsp2\u002Fes9plus\u002FauthenticateClient (待实现)        │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│   - ES9+ \u002Fgsma\u002Frsp2\u002Fes9plus\u002FgetBoundProfilePackage (待实现)   │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│   - 卡在：ProfilePackagingProvider = UnavailableProvider      │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>│   - 原因：没有 GSMA 签发的 CERT.DPauth.ECDSA                 │\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>└──────────────────────────────────────────────────────────────┘\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>\u003Cstrong>左边那套\u003C\u002Fstrong>（rtpd + phone-*）是&rdquo;应用层 VoIP over any-IP&rdquo;，我可以做，可以给你用，可以做完整加密协议，可以用 iOS CallKit 让体验接近原生电话。但它\u003Cstrong>永远进不了运营商拨号盘\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Cp>\u003Cstrong>右边那套\u003C\u002Fstrong>（sm-dp + MiniLPA）是&rdquo;eSIM 个人化通道&rdquo;，我可以做完整协议层，但\u003Cstrong>永远拿不到 GSMA 签发的 SM-DP+ 证书\u003C\u002Fstrong>，所以商用 eUICC 永远不信我。\u003C\u002Fp>\n\n\u003Cp>这两个栈的交集：\u003Cstrong>没有\u003C\u002Fstrong>。它们解决不同的问题，互不替代。\u003C\u002Fp>\n\n\u003Chr \u002F>\n\n\u003Ch2>9. 结论\u003C\u002Fh2>\n\n\u003Cp>\u003Cstrong>TL;DR（再说一遍）\u003C\u002Fstrong>：普通人自建&rdquo;运营商级 WiFi Calling&rdquo;——\u003Cstrong>不行\u003C\u002Fstrong>。三重锁死：\u003C\u002Fp>\n\n\u003Col>\n\u003Cli>\u003Cstrong>eUICC 证书信任链（首道锁，最硬）\u003C\u002Fstrong> —— GSMA Root CI1 公钥烧死在 ECASD，私有证书行不通，软件改不了，这是硅片级的死结。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>运营商 IMS 核心网\u003C\u002Fstrong> —— 凭据在 ISIM @ UICC，ePDG \u002F HSS \u002F IBCF \u002F IPX 全部是企业级专有基础设施。\u003C\u002Fli>\n\u003Cli>\u003Cstrong>iOS \u002F Android WiFi Calling 入口\u003C\u002Fstrong> —— carrier entitlement + carrier bundle + modem 固件，平台权力决定谁能被点亮。\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>普通人能做的最远边界：\u003Cstrong>应用层 VoIP over WiFi\u003C\u002Fstrong>。它不是 WiFi Calling，不能进运营商拨号盘，不能跨运营商互通 PSTN 出园区，但仍能在 IP 范围内提供良好体验——我手头的 \u003Ccode class=\"inline-code\">rtpd\u003C\u002Fcode> + \u003Ccode class=\"inline-code\">phone-android\u003C\u002Fcode> \u002F \u003Ccode class=\"inline-code\">phone-ios\u003C\u002Fcode> 就是这个边界的一个真实案例。\u003C\u002Fp>\n\n\u003Cp>&ldquo;那能不能做私有 IMS 网？&rdquo;——企业可以，个人不行，仍要 GSMA 注册 + 合规审计 + 法人主体。\u003C\u002Fp>\n\n\u003Cp>&ldquo;那 GSMA SGP.22 &lsquo;999&rsquo; 是不是个人通道？&rdquo;——不是。简化通道仍是企业通道，仍然走 GSMA PKI。\u003C\u002Fp>\n\n\u003Cp>&ldquo;那把 GSMA Root CI 装到手机系统信任根里能不能绕过？&rdquo;——不能。iOS \u002F Android 系统信任根管不到 eUICC，ECASD 是独立的 JavaCard 安全元件。\u003C\u002Fp>\n\n\u003Cp>如果你想做&rdquo;普通人能用的语音通话网络&rdquo;，我的真实建议：\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>\u003Cstrong>不要碰 eSIM \u002F SM-DP+ 这一条路\u003C\u002Fstrong>，除非你是企业、愿意投百万级 + 半年到一年走 SAS-SM\u003C\u002Fli>\n\u003Cli>\u003Cstrong>走应用层 VoIP\u003C\u002Fstrong>——IP 网络上能跑通就好，iOS CallKit 让体验接近原生电话，体验上限就在这里\u003C\u002Fli>\n\u003Cli>\u003Cstrong>要合法合规\u003C\u002Fstrong>：不要碰真实 PSTN 互通，不要碰紧急呼叫责任，不要碰 PLMN 标识\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>如果你想做&rdquo;私有 5G\u002FLTE + 私有 IMS&rdquo;——这是个\u003Cstrong>百万级起步的企业项目\u003C\u002Fstrong>，不是个人项目，请按企业路径走。\u003C\u002Fp>\n\n\u003Chr \u002F>\n\n\u003Ch2>10. 参考资料\u003C\u002Fh2>\n\n\u003Cul>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.gsma.com\u002Fesim\u002F\">GSMA - eSIM &amp; SAS\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.gsma.com\u002Fnewsroom\u002Fblog\u002Fott-calling-protocol-analysis\u002F\">GSMA blog - OTT Calling Protocol Analysis\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fsupport.apple.com\u002Fen-us\u002F108066\">Apple Support - Use Wi-Fi calling on your iPhone\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fsource.android.com\u002Fdocs\u002Fcore\u002Fconnect\u002Fwifi-wifi-calling\">Android Source - Wi-Fi calling\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002FvoLTEinfo.com\u002Fwi-fi-calling-architecture\u002F\">VoLTE Info - Wi-Fi Calling Architecture\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.mavenir.com\u002Fblog\u002Fwifi-calling-vowifi-vs-ott-voip\">Mavenir - WiFi Calling (VoWiFi) vs OTT VoIP\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.ericsson.com\u002Fen\u002Fblog\u002F2024\u002F02\u002Fwifi-calling-deployment\">Ericsson - WiFi Calling Deployment\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.cisco.com\u002Fc\u002Fen\u002Fus\u002Fproducts\u002Fwireless\u002Fepdg-evolved-packet-data-gateway\u002Findex.html\">Cisco - ePDG (Evolved Packet Data Gateway)\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fengineering.fb.com\u002F2023\u002F03\u002F14\u002Fwhatsapp-calling-architecture\u002F\">Facebook Engineering - WhatsApp Calling Architecture\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fsignal.org\u002Fblog\u002Fcalling-2\u002F\">Signal Blog - Calling 2.0\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.etsi.org\u002Fdeliver\u002Fetsi_TS\u002F123200_123299\u002F123228\u002F\">3GPP TS 23.228 - IMS\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.etsi.org\u002Fdeliver\u002Fetsi_TS\u002F123400_123499\u002F123402\u002F\">3GPP TS 23.402 - Non-3GPP Access\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.etsi.org\u002Fdeliver\u002Fetsi_TS\u002F124500_124599\u002F124502\u002F16.07.00_60\u002Fts_124502v160700p.pdf\">3GPP TS 24.502 - Non-3GPP Access Session Management\u003C\u002Fa>\u003C\u002Fli>\n\u003Cli>\u003Ca href=\"https:\u002F\u002Fwww.arib.or.jp\u002Fenglish\u002Fhtml\u002Foverview\u002Fdoc\u002FSTD-T63v9_20\u002F5_Appendix\u002FRel10\u002F33\u002F33402-a10.pdf\">3GPP TS 33.402 - IKEv2 \u002F IPsec Security\u003C\u002Fa>\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Chr \u002F>\n\n\u003Ch2>11. 写在最后\u003C\u002Fh2>\n\n\u003Cp>我搭 \u003Ccode class=\"inline-code\">sm-dp\u003C\u002Fcode> 的初衷是&rdquo;我想搞清楚 eSIM 到底是怎么工作的&rdquo;。结果搞清楚之后，发现这条路对个人\u003Cstrong>没有商用落点\u003C\u002Fstrong>。\u003C\u002Fp>\n\n\u003Cp>但\u003Cstrong>搞清楚的过程本身有价值\u003C\u002Fstrong>。我跑完了协议层，撞上了 eUICC 信任链这堵墙，写下这篇文章，至少能让下一个想&rdquo;自建 eSIM 运营商&rdquo;的朋友少走三个月弯路。\u003C\u002Fp>\n\n\u003Cp>对普通开发者来说，\u003Cstrong>应用层 VoIP over IP\u003C\u002Fstrong> 仍然是 IP 网络上能做的事情里体验最好的一条路。iOS CallKit 让它看起来像电话，Signal Protocol 让它端到端加密，WebRTC 让浏览器免装 App。它\u003Cstrong>不是\u003C\u002Fstrong>运营商级 WiFi Calling，但它\u003Cstrong>是\u003C\u002Fstrong>真正可用的语音通信。\u003C\u002Fp>\n\n\u003Cp>这是我能给出的最诚实的答案。\u003C\u002Fp>\n",{"id":19,"title":20,"uri":21,"author":10,"tags":22,"created_at":25,"view_count":26,"content":27},126,"蒹葭苍苍，白露为霜，又是一年霜降","\u002Fjianjiacangcang-bailuweishuang",[23,24],"故乡","霜","2026-01-03 14:05:40",0,"\u003Cp>\u003Cimg src=\"https:\u002F\u002Fimage.duguying.net\u002F2026\u002F01\u002F862vbg8b.webp\" alt=\"862vbg8b\" \u002F>\u003C\u002Fp>\n\n\u003Cp>没有冬季的严寒，又怎么懂得春暖花开\u003Cbr \u002F>\n没有夏季的酷暑，又怎么理解秋高气爽\u003Cbr \u002F>\n我不喜欢南方的一年一季\u003Cbr \u002F>\n只怀念故乡的春夏秋冬\u003C\u002Fp>\n",{"id":29,"title":30,"uri":31,"author":10,"tags":32,"created_at":36,"view_count":37,"content":38},122,"初学rust，tokio::spawn中loop-sleep无法使用ctrl+c终止进程的原因","why-ctrl-c-cannot-terminate-process-with-spawn-loop-sleep",[33,34,35],"tokio","ctrl-c","终止进程","2025-04-07 16:11:55",117,"\u003Cp>在tokio::spawn中起了一个新协程，然后在代码中执行sleep死循环，发现程序运行时，主线程中无法通过ctrl+c终止进程。代码如下\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">#[\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">actix_web::main\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">]\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">async\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">main\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>-&gt; \u003Cspan style=\"color:#f0883e;font-weight:bold\">std\u003C\u002Fspan>::io::Result\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>()\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u002F\u002F 解析命令行参数\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>args\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>Arguments::parse();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">if\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>args.version\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>print_ver();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">return\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>Ok(());\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u002F\u002F 初始化摄像头\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>spawn(\u003Cspan style=\"color:#ff7b72\">async\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">move\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>info!\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">camera capture started\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>camera_capture().\u003Cspan style=\"color:#ff7b72\">await\u003C\u002Fspan>;\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>});\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>c\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>config::get_config();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>listen\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>c.tlps.as_ref().unwrap().listen.as_ref().unwrap();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>println!\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>(\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">listen http:\u002F\u002F\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">{}\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>listen);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>HttpServer::new(\u003Cspan style=\"color:#ff7b72;font-weight:bold\">|\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">|\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>App::new().service(camera_settings).service(\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">            \u003C\u002Fspan>SwaggerUi::new(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">\u002Fswagger-ui\u002F{_:.*}\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>).url(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">\u002Fapi-docs\u002Fopenapi.json\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>ApiDoc::openapi()),\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>)\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>})\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>.bind(listen)\u003Cspan style=\"color:#ff7b72;font-weight:bold\">?\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>.run()\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>.\u003Cspan style=\"color:#ff7b72\">await\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">async\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">camera_capture\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>tlps_cfg\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>config::get_config().tlps.as_ref().unwrap();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">loop\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>std::thread::sleep(std::time::Duration::from_secs(\u003Cspan style=\"color:#a5d6ff\">1\u003C\u002Fspan>));\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>print_ver();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>现象，在终端中按下\u003Ccode class=\"inline-code\">Ctrl+C\u003C\u002Fcode>无法退出进程，如下\u003C\u002Fp>\n\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fimage.duguying.net\u002F2025\u002F04\u002Fhg6ndsox.png\" alt=\"hg6ndsox\" \u002F>\u003C\u002Fp>\n\n\u003Cp>最后发现原因是\u003Ccode class=\"inline-code\">tokio\u003C\u002Fcode>拉起的线程(协程)中不能使用\u003Ccode class=\"inline-code\">std::thread\u003C\u002Fcode>的\u003Ccode class=\"inline-code\">sleep\u003C\u002Fcode>方法进行休眠，而是应该使用\u003Ccode class=\"inline-code\">tokio\u003C\u002Fcode>的\u003Ccode class=\"inline-code\">sleep\u003C\u002Fcode>方法休眠。我这里是\u003Ccode class=\"inline-code\">actix-web\u003C\u002Fcode>，虽然它是基于\u003Ccode class=\"inline-code\">tokio\u003C\u002Fcode>的，但是，他也有对应的\u003Ccode class=\"inline-code\">sleep\u003C\u002Fcode>方法，应该改为如下方式\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#ff7b72\">loop\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>actix_web::rt::time::sleep(std::time::Duration::from_secs(\u003Cspan style=\"color:#a5d6ff\">1\u003C\u002Fspan>)).\u003Cspan style=\"color:#ff7b72\">await\u003C\u002Fspan>;\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>print_ver();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>再使用ctrl-c，能够正常终止进程了。\u003C\u002Fp>\n",{"id":40,"title":41,"uri":42,"author":10,"tags":43,"created_at":46,"view_count":47,"content":48},120,"初学rust，trait的一些使用限制","rust-some-limit-for-trait",[44,45],"rust","trait","2024-06-12 16:08:51",187,"\u003Cp>rust中，trait相当于其他语言中的接口。例如json序列化，需要实现\u003Ccode class=\"inline-code\">Serialize\u003C\u002Fcode>这个trait，但是，这里存在如下三个限制：\u003C\u002Fp>\n\n\u003Col>\n\u003Cli>要实现的 trait 和类型都在当前 crate 中定义。\u003C\u002Fli>\n\u003Cli>要实现的 trait 在当前 crate 中定义，而类型在外部 crate 中定义。\u003C\u002Fli>\n\u003Cli>要实现的类型在当前 crate 中定义，而 trait 在外部 crate 中定义。\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cp>也就是说，你想要对一个结构体去实现某个trait，要么，这个结构体是你的（也就是在你当前crate包中），要么，这个trait是你定义的，两者至少有一个是你自己的，你才能对这个结构体实现这个指定的trait。\u003C\u002Fp>\n\n\u003Cp>如果，trait和结构体都不是你实现的，你将无法为这个外部结构体实现外部trait。例如，我想为TcpStream实现Serialize，直接写，会报错\u003C\u002Fp>\n\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fimage.duguying.net\u002F2024\u002F06\u002Fsfgdut7l.webp\" alt=\"sfgdut7l\" \u002F>\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#f85149\">`\u003C\u002Fspan>mio::net::TcpStream\u003Cspan style=\"color:#f85149\">`\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>is\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>not\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>defined\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">in\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>the\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>current\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>craterustcE0117\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>main.rs(\u003Cspan style=\"color:#a5d6ff\">24\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">1\u003C\u002Fspan>): \u003Cspan style=\"color:#f0883e;font-weight:bold\">original\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>diagnostic\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>只能通过包裹TcpStream来实现了，总之，为外部结构体实现外部trait就是行不通。\u003C\u002Fp>\n",{"id":50,"title":51,"uri":52,"author":10,"tags":53,"created_at":55,"view_count":56,"content":57},119,"初学rust，rust数字类型转换不安全","rust-integer-type-force-cast-not-safe",[44,54],"类型转换","2024-05-28 15:04:09",250,"\u003Cp>数字类型转换是所有语言中存在的一项操作。比如golang的float64转float32，用\u003Ccode class=\"inline-code\">float32(v64)\u003C\u002Fcode>即可实现，但是这个转换在\u003Ccode class=\"inline-code\">v64\u003C\u002Fcode>的值只要不超过float32的上限，就可以安全转换。但是，这在rust中似乎行不通。\u003C\u002Fp>\n\n\u003Cp>我在一个实践程序里在结构体中定义了一系列的f32,i32字段，然后用\u003Ccode class=\"inline-code\">parse::&lt;f32&gt;()\u003C\u002Fcode>从字符串解析出f32，这一切都正常，我单独单测代码打印过这个结果，正确的读取了数字；但是当我将其进行了一系列的累加之后，转为f64再json序列化，发现居然变成了null，然后我debug打印发现，数字变成\u003Ccode class=\"inline-code\">inf\u003C\u002Fcode>了，也就是\u003Ccode class=\"inline-code\">f64::INFINITY\u003C\u002Fcode>，我类型转换过程中用的就是\u003Ccode class=\"inline-code\">as f64\u003C\u002Fcode>。\u003C\u002Fp>\n\n\u003Cp>后来我索性将定义的f32改为了f64，最后它又正常了。这说明\u003Ccode class=\"inline-code\">as\u003C\u002Fcode>操作并没有想象中那么安全。\u003C\u002Fp>\n",{"id":59,"title":60,"uri":61,"author":10,"tags":62,"created_at":65,"view_count":66,"content":67},118,"初学rust，多线程中读写全局变量","read-and-write-global-var-in-multi-thread-for-rust",[63,64,44],"全局变量","读写","2024-04-25 22:51:33",286,"\u003Cp>参考上一篇\u003Ca href=\"https:\u002F\u002Fwww.duguying.net\u002Farticle\u002Frust-how-to-set-and-get-global-variable-in-runtime\">《初学rust，如何实现在运行时对全局变量设置和读取》\u003C\u002Fa>，文章中说，可以用\u003Ccode class=\"inline-code\">OnceCell\u003C\u002Fcode>来定义全局变量，然后就可以对全局变量进行读写，实际上，我发现\u003Ccode class=\"inline-code\">OnceCell\u003C\u002Fcode>的\u003Ccode class=\"inline-code\">set()\u003C\u002Fcode>方法只能调用一次，如果你试图第二次调用\u003Ccode class=\"inline-code\">set()\u003C\u002Fcode>来修改已经设定好的值，将会报错，设置值就失败了。\u003C\u002Fp>\n\n\u003Cp>由于这一切都是为了读写的并发安全。所以\u003Ccode class=\"inline-code\">OnceCell\u003C\u002Fcode>是无法实现读写全局变量的。要实现读写全局变量，应该使用\u003Ccode class=\"inline-code\">RwLock\u003C\u002Fcode>。如下\u003C\u002Fp>\n\n\u003Cp>定义全局变量\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#ff7b72\">static\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#79c0ff;font-weight:bold\">WORKER_PID\u003C\u002Fspan>: \u003Cspan style=\"color:#f0883e;font-weight:bold\">RwLock\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>Option\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">u32\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>RwLock::new(None);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>读写全局变量\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u002F\u002F 写入\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">store_worker_pid\u003C\u002Fspan>(pid: \u003Cspan style=\"color:#ff7b72\">u32\u003C\u002Fspan>)\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">mut\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>data\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#79c0ff;font-weight:bold\">WORKER_PID\u003C\u002Fspan>.write().unwrap();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">*\u003C\u002Fspan>data\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>Some(pid);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u002F\u002F 读取\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>data\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#79c0ff;font-weight:bold\">WORKER_PID\u003C\u002Fspan>.read().unwrap();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>cpid\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>data.unwrap();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>这里的读写就可以多次进行了。可以看到，这里使用了读写锁，写的时候会先通过\u003Ccode class=\"inline-code\">write()\u003C\u002Fcode>方法获取写入锁实例，然后对其赋值写入。读取的时候会先通过\u003Ccode class=\"inline-code\">read()\u003C\u002Fcode>方法获取读取锁实例，然后取里面的值。这样一来就实现了全局变量的读写。\u003C\u002Fp>\n",{"id":69,"title":70,"uri":71,"author":10,"tags":72,"created_at":76,"view_count":77,"content":78},115,"初学rust，不允许遍历过程中修改HashMap","in-rust-its-not-allow-to-modify-content-of-hashmap-while-iter-it",[44,73,74,75],"HashMap","遍历","修改","2023-12-28 21:07:42",241,"\u003Cp>先看一段代码\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">#[\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">test\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">]\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">test_hash_map\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">mut\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>mp: \u003Cspan style=\"color:#f0883e;font-weight:bold\">HashMap\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&amp;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">str\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&amp;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">str\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>HashMap::new();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>x\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>mp.keys().clone();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">for\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>k\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">in\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>x\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k2\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k3\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k2\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k3\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>看，它报错\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>error[E0502]: cannot borrow `mp` as mutable because it is also borrowed as immutable\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>   --&gt; src\u002Fprocess.rs:153:9\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    |\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>151 |     let x = mp.keys().clone();\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    |             -- immutable borrow occurs here\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>152 |     for k in x {\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    |              - immutable borrow later used here\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>153 |         mp.insert(&#34;k2&#34;, &#34;v1&#34;);\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    |         ^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>报错告诉我们，不允许将\u003Ccode class=\"inline-code\">mp\u003C\u002Fcode>作为mutable，因为它已经用于immutable了。难道，HashMap插入数据完毕，开始读取数据之后，不能再次插入数据了？我一开始这么怀疑，不应该啊，于是代码改成这样\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">#[\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">test\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">]\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">test_hash_map\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">mut\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>mp: \u003Cspan style=\"color:#f0883e;font-weight:bold\">HashMap\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&amp;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">str\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&amp;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">str\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>HashMap::new();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.keys().clone();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k2\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k3\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>正常了，没报错了。看样子问题出在这个\u003Ccode class=\"inline-code\">for ..in..\u003C\u002Fcode>中。仔细分析代码，我在for中遍历了它的key，或者说我正在将这个HashMap中的数据拿出来，这时候，我在for中尝试往这个HashMap中写入数据，写入数据会让这个HashMap发生变更，这里第一感让我觉得可能有问题。比如我HashMap中有10个元素，我在遍历它，然后在for中间插入新元素，那么是不是有下列问题：1，我的HashMap会不会越遍历越多，会不会永远无法遍历完；2，HashMap是无序的，我将新元素插入HashMap中，会不会导致我已经遍历过的数据由于插入新数据，导致再次被读取出来，因为它可能位置发生变化了嘛。\u003C\u002Fp>\n\n\u003Cp>其实这就是数据竞争和它带来的不确定性问题，rust作为一个内存安全第一的编程语言，编译器会教你做人。\u003C\u002Fp>\n\n\u003Cp>于是，我再改\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">#[\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">test\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">]\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">test_hash_map\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">mut\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>mp: \u003Cspan style=\"color:#f0883e;font-weight:bold\">HashMap\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&amp;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">str\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&amp;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">str\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>HashMap::new();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>x: Vec\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&amp;\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">str\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>mp.keys().map(\u003Cspan style=\"color:#ff7b72;font-weight:bold\">|\u003C\u002Fspan>k\u003Cspan style=\"color:#ff7b72;font-weight:bold\">|\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">*\u003C\u002Fspan>k).collect();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">for\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>k\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">in\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>x\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k2\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k3\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k2\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>mp.insert(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">k3\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">v1\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>);\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>这回正常了。可以看到我的操作\u003Ccode class=\"inline-code\">let x: Vec&lt;&amp;str&gt; = mp.keys().map(|k| *k).collect();\u003C\u002Fcode>是将keys()拿到的\u003Ccode class=\"inline-code\">Keys\u003C\u002Fcode>迭代器（仍旧从前面的HashMap里迭代）通过\u003Ccode class=\"inline-code\">.collect()\u003C\u002Fcode>方法将迭代器里的元素“倒”入到\u003Ccode class=\"inline-code\">Vec&lt;&amp;str&gt;\u003C\u002Fcode>，这样这个\u003Ccode class=\"inline-code\">Vec\u003C\u002Fcode>就是一个独立与HashMap内存空间之外的变量，再基于这个Vec进行遍历，就可以避免“边遍历边修改的”的情况了。\u003C\u002Fp>\n\n\u003Cp>那么大家可以思考一下，其他语言，比如golang，遇到这种情况是怎么处理的呢。\u003C\u002Fp>\n",{"id":80,"title":81,"uri":82,"author":10,"tags":83,"created_at":86,"view_count":87,"content":88},114,"初学rust，踩坑私有依赖","private-dependencies-in-rust",[44,84,85],"依赖","git","2023-12-27 22:28:21",431,"\u003Cp>最近尝试在一个项目中引用一个私有crate，这个crate是一个lib，然后存储在私有git仓库中，并且这个git平台不支持rust的crate-index.\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>\u003Cstrong>第一坑，引用git依赖\u003C\u002Fstrong>\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>于是我在我的项目中这样引用外部依赖包\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>[package]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>name = \u003Cspan style=\"color:#a5d6ff\">&#34;las&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>version = \u003Cspan style=\"color:#a5d6ff\">&#34;0.1.1&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>authors = [\u003Cspan style=\"color:#a5d6ff\">&#34;....&#34;\u003C\u002Fspan>]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>edition = \u003Cspan style=\"color:#a5d6ff\">&#34;2018&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>[dependencies]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>elasticsearch = { version = \u003Cspan style=\"color:#a5d6ff\">&#34;8.5.0-alpha.1&#34;\u003C\u002Fspan>, default-features = \u003Cspan style=\"color:#79c0ff\">false\u003C\u002Fspan>, features = [\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    \u003Cspan style=\"color:#a5d6ff\">&#34;rustls-tls&#34;\u003C\u002Fspan>,\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>] }\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>logwatcher = \u003Cspan style=\"color:#a5d6ff\">&#34;0.1.1&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>json = \u003Cspan style=\"color:#a5d6ff\">&#34;*&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>reqwest = { version = \u003Cspan style=\"color:#a5d6ff\">&#34;0.11.12&#34;\u003C\u002Fspan>, default-features = \u003Cspan style=\"color:#79c0ff\">false\u003C\u002Fspan>, features = [\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    \u003Cspan style=\"color:#a5d6ff\">&#34;blocking&#34;\u003C\u002Fspan>,\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>    \u003Cspan style=\"color:#a5d6ff\">&#34;rustls-tls&#34;\u003C\u002Fspan>,\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>] }\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>toml = \u003Cspan style=\"color:#a5d6ff\">&#34;0.8.6&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>serde_derive = \u003Cspan style=\"color:#a5d6ff\">&#34;1.0.177&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>serde = \u003Cspan style=\"color:#a5d6ff\">&#34;1.0.177&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>serde_json = \u003Cspan style=\"color:#a5d6ff\">&#34;1.0.104&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>once_cell = \u003Cspan style=\"color:#a5d6ff\">&#34;1.18.0&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>futures = \u003Cspan style=\"color:#a5d6ff\">&#34;0.3&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>tokio = { version = \u003Cspan style=\"color:#a5d6ff\">&#34;*&#34;\u003C\u002Fspan>, features = [\u003Cspan style=\"color:#a5d6ff\">&#34;full&#34;\u003C\u002Fspan>] }\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>uuid = { version = \u003Cspan style=\"color:#a5d6ff\">&#34;1.4&#34;\u003C\u002Fspan>, features = [\u003Cspan style=\"color:#a5d6ff\">&#34;v4&#34;\u003C\u002Fspan>, \u003Cspan style=\"color:#a5d6ff\">&#34;fast-rng&#34;\u003C\u002Fspan>, \u003Cspan style=\"color:#a5d6ff\">&#34;macro-diagnostics&#34;\u003C\u002Fspan>] }\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>chrono = \u003Cspan style=\"color:#a5d6ff\">&#34;0.4&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>core_affinity = \u003Cspan style=\"color:#a5d6ff\">&#34;0.8.0&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>geoip = { git = \u003Cspan style=\"color:#a5d6ff\">&#34;ssh:\u002F\u002Fgit@git.xxxx.com\u002Fxxxx\u002Ftgeoip.git&#34;\u003C\u002Fspan>, branch = \u003Cspan style=\"color:#a5d6ff\">&#34;master&#34;\u003C\u002Fspan>, package = \u003Cspan style=\"color:#a5d6ff\">&#34;geoip&#34;\u003C\u002Fspan> }\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>nix = \u003Cspan style=\"color:#a5d6ff\">&#34;0.26&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>libc = \u003Cspan style=\"color:#a5d6ff\">&#34;0.2.146&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>clap = { version = \u003Cspan style=\"color:#a5d6ff\">&#34;4.4.2&#34;\u003C\u002Fspan>, features = [\u003Cspan style=\"color:#a5d6ff\">&#34;derive&#34;\u003C\u002Fspan>] }\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>log4rs = { version = \u003Cspan style=\"color:#a5d6ff\">&#34;1.2.0&#34;\u003C\u002Fspan> }\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>log = { version = \u003Cspan style=\"color:#a5d6ff\">&#34;0.4.20&#34;\u003C\u002Fspan> }\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>[build-dependencies]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>regex = \u003Cspan style=\"color:#a5d6ff\">&#34;1.6.0&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>需要注意的是，我\u003Ccode class=\"inline-code\">git@git.xxxx.com:xxxx\u002Ftgeoip.git\u003C\u002Fcode>仓库中是一个workspace，里面有2个子项，一个是可执行bin项目，叫做\u003Ccode class=\"inline-code\">regen\u003C\u002Fcode>，一个是外部可依赖包，叫做\u003Ccode class=\"inline-code\">geoip\u003C\u002Fcode>（后续实践表明同一个workspace里面的子项引用就应该这么干，git路径是同一个，使用package指定子项的包名），我这样引入之后，发现死活拉取不了私有git依赖。报错没权限拉取，git鉴权失败\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>error: failed to get `geoip` as a dependency of package `las v0.1.1 (\u002Fdata\u002Fcode\u002Frust\u002Flas)`\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>Caused by:\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>  failed to load source for dependency `geoip`\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>Caused by:\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>  Unable to update ssh:\u002F\u002Fgit@git.xxxx.com\u002Fxxxx\u002Ftgeoip.git?branch=master#e41c5279\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>Caused by:\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>  failed to clone into: \u002Froot\u002F.cargo\u002Fgit\u002Fdb\u002Ftgeoip-9094aceea5940357\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>Caused by:\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>  failed to authenticate when downloading repository\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>  * attempted ssh-agent authentication, but no usernames succeeded: `git`\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>  if the git CLI succeeds then `net.git-fetch-with-cli` may help here\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>  https:\u002F\u002Fdoc.rust-lang.org\u002Fcargo\u002Freference\u002Fconfig.html#netgit-fetch-with-cli\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>Caused by:\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>  no authentication methods succeeded\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>各方查询后，都是建议添加\u003Ca href=\"https:\u002F\u002Fdoc.rust-lang.org\u002Fcargo\u002Freference\u002Fconfig.html#netgit-fetch-with-cli\">这个\u003C\u002Fa>选项，我开始还没搞清楚加在哪儿，但是发现使用环境变量好使。各方尝试后：\u003C\u002Fp>\n\n\u003Cp>添加到\u003Ccode class=\"inline-code\">~\u002F.cargo\u002Fconfig\u003C\u002Fcode>文件中，如下\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>[net]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>git-fetch-with-cli = \u003Cspan style=\"color:#79c0ff\">true\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>然后再次执行\u003Ccode class=\"inline-code\">cargo build\u003C\u002Fcode>无需环境变量即可。（前提是你本地已经配置好了访问\u003Ccode class=\"inline-code\">ssh:\u002F\u002Fgit@git.xxxx.com\u002Fxxxx\u002Ftgeoip.git\u003C\u002Fcode>的ssh key）\u003C\u002Fp>\n\n\u003Cul>\n\u003Cli>\u003Cstrong>第二坑，外部依赖包要申明为\u003Ccode class=\"inline-code\">rlib\u003C\u002Fcode>\u003C\u002Fstrong>\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>我在这个bin项目中引用\u003Ccode class=\"inline-code\">geoip\u003C\u002Fcode>，发现一直报错\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>failed to resolve: use of undeclared crate or module `geoip`\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>百思不得其解，最后发现是我依赖包申明类型的问题，我\u003Ccode class=\"inline-code\">Cargo.toml\u003C\u002Fcode>申明的是\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>[package]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>....\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>[lib]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>name = \u003Cspan style=\"color:#a5d6ff\">&#34;geoip&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>crate-type = [\u003Cspan style=\"color:#a5d6ff\">&#34;staticlib&#34;\u003C\u002Fspan>, \u003Cspan style=\"color:#a5d6ff\">&#34;cdylib&#34;\u003C\u002Fspan>]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>[dependencies]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>bincode = \u003Cspan style=\"color:#a5d6ff\">&#34;1.3.3&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>serde = { version = \u003Cspan style=\"color:#a5d6ff\">&#34;1.0.193&#34;\u003C\u002Fspan>, features = [\u003Cspan style=\"color:#a5d6ff\">&#34;derive&#34;\u003C\u002Fspan>] }\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>实际\u003Ccode class=\"inline-code\">lib\u003C\u002Fcode>应该申明\u003Ccode class=\"inline-code\">rlib\u003C\u002Fcode>类型，才能被正常引用，改为如下，更新依赖，就没报错了\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>[package]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>....\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>[lib]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>name = \u003Cspan style=\"color:#a5d6ff\">&#34;geoip&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>crate-type = [\u003Cspan style=\"color:#a5d6ff\">&#34;staticlib&#34;\u003C\u002Fspan>, \u003Cspan style=\"color:#a5d6ff\">&#34;cdylib&#34;\u003C\u002Fspan>, \u003Cspan style=\"color:#a5d6ff\">&#34;rlib&#34;\u003C\u002Fspan>]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>[dependencies]\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>bincode = \u003Cspan style=\"color:#a5d6ff\">&#34;1.3.3&#34;\u003C\u002Fspan>\n\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>serde = { version = \u003Cspan style=\"color:#a5d6ff\">&#34;1.0.193&#34;\u003C\u002Fspan>, features = [\u003Cspan style=\"color:#a5d6ff\">&#34;derive&#34;\u003C\u002Fspan>] }\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n",{"id":90,"title":91,"uri":92,"author":10,"tags":93,"created_at":96,"view_count":97,"content":98},113,"初试riscv机器","try-riscv-machine",[94,95],"riscv64","linux","2023-12-18 09:28:29",135,"\u003Cp>最近入手了一块riscv64的开发板，是某宝上入手的StarFive2，准备拿它当做riscv64架构的服务器用来编译程序。开始刷上的是官方给的debian系统，成功启动了，nvme正常，但是有两个问题，第一个是\u003Ccode class=\"inline-code\">apt update\u003C\u002Fcode>会报签名错误，第二个是不支持docker，想要让它支持docker，需要自己编译升级内核，非常麻烦。于是决定换用ubuntu版本的镜像。\u003C\u002Fp>\n\n\u003Cp>根据\u003Ca href=\"https:\u002F\u002Fwiki.ubuntu.com\u002FRISC-V\u002FStarFive%20VisionFive%202\">官方教程\u003C\u002Fa>，需要先将板载固件刷新到ubuntu社区的指定版本，从\u003Ca href=\"https:\u002F\u002Flaunchpad.net\u002F~ubuntu-risc-v-team\u002F+archive\u002Fubuntu\u002Frelease\u002F+packages\">这里\u003C\u002Fa>下载，需要注意的是，目前（2023\u002F12\u002F18）最新版的固件（\u003Ccode class=\"inline-code\">u-boot-starfive-2024.01~rc4-0ubuntu1~ppa1\u003C\u002Fcode>这个）会导致\u003Ccode class=\"inline-code\">kernel panic\u003C\u002Fcode>，需要下载下面的版本\u003C\u002Fp>\n\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fimage.duguying.net\u002F2023\u002F12\u002F2flhl8vj.webp\" alt=\"2flhl8vj\" \u002F>，展开后，用wget下载deb安装包，找个ubuntu机器，用\u003Ccode class=\"inline-code\">dpkg -x\u003C\u002Fcode>解包，将里面的下面两个文件上传到\u003Ccode class=\"inline-code\">sdcard.img\u003C\u002Fcode>刷机包启动的系统里面\u003C\u002Fp>\n\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fimage.duguying.net\u002F2023\u002F12\u002Fg80xfqba.webp\" alt=\"g80xfqba\" \u002F>\u003C\u002Fp>\n\n\u003Cp>更新SPL\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>flashcp -v u-boot-spl.bin.normal.out \u002Fdev\u002Fmtd0\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>更新U-Boot\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>flashcp -v u-boot.itb \u002Fdev\u002Fmtd2\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>然后安装ubuntu系统，注意从\u003Ca href=\"https:\u002F\u002Fcdimage.ubuntu.com\u002Freleases\u002F23.04\u002Frelease\u002F\">这里\u003C\u002Fa>选择下面版本的系统\u003C\u002Fp>\n\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fimage.duguying.net\u002F2023\u002F12\u002Fjerua3mn.webp\" alt=\"jerua3mn\" \u002F>\u003C\u002Fp>\n\n\u003Cp>使用etcher将固件写入到sd卡，插入sd卡，启动，正常情况就可以启动成功。\u003C\u002Fp>\n\n\u003Cp>在这中间我踩过一些坑，在此也一起分享一下。首先是hdmi插在4k屏无法显示，这样一来，我就不知道当前系统启动得怎么样了，因此\u003Ccode class=\"inline-code\">kernel panic\u003C\u002Fcode>了，我也不知道，后来只能使用usb转串口用电脑连接串口，将串口控制台当做显示器用，才知道启动过程中发生了什么，串口连接如下图\u003C\u002Fp>\n\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fimage.duguying.net\u002F2023\u002F12\u002F3atnih7j.png\" alt=\"3atnih7j\" \u002F>\u003C\u002Fp>\n\n\u003Cp>从串口控制台看到如下图（学到了，串口还能当显示器用）\u003C\u002Fp>\n\n\u003Cp>\u003Cimg src=\"https:\u002F\u002Fimage.duguying.net\u002F2023\u002F12\u002Fy2m6970v.webp\" alt=\"y2m6970v\" \u002F>\u003C\u002Fp>\n\n\u003Cp>可以看到，启动过程卡死在\u003Ccode class=\"inline-code\">kernel panic\u003C\u002Fcode>，我这才知道是内核崩溃了。看起来是\u003Ccode class=\"inline-code\">u-boot-starfive-2024.01~rc4-0ubuntu1~ppa1\u003C\u002Fcode>这个版本的固件，对现有的\u003Ccode class=\"inline-code\">23.04\u003C\u002Fcode>版本的系统兼容性有问题，所以我换了上个版本的固件，成功启动。\u003C\u002Fp>\n\n\u003Cp>到此，在StarFive2上成功的安装上了ubuntu系统，docker也成功的安装，不过这个版本的板载固件\n有个问题，就是无法识别nvme硬盘，希望下个正常版本能够支持。\u003C\u002Fp>\n",{"id":100,"title":101,"uri":102,"author":10,"tags":103,"created_at":105,"view_count":106,"content":107},112,"初学rust，错误处理","rust-beginners-guide-error-handle",[104,44],"错误处理","2023-12-14 14:55:54",172,"\u003Cp>rust中如何比较优雅的进行错误处理，这是一直以来困扰我的一个问题。最近写了一个ip地址库查询包，于是在其中实践了一下自定义错误、错误抛出等处理。\u003C\u002Fp>\n\n\u003Cp>rust中，如果一个函数需要返回错误，那么应该用\u003Ccode class=\"inline-code\">Result\u003C\u002Fcode>包裹返回值，\u003Ccode class=\"inline-code\">Result\u003C\u002Fcode>定义如下\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#ff7b72\">enum\u003C\u002Fspan> Result\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>T,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>E\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>Ok(T),\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>Err(E),\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>通常，\u003Ccode class=\"inline-code\">Result\u003C\u002Fcode>中的第二个参数就是错误。比如\u003Ccode class=\"inline-code\">Result&lt;String, String&gt;\u003C\u002Fcode>，那么它的错误就是一个String类型的值，如下\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u002F\u002F 返回了正常值hi\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">find\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>-&gt; Result\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>String,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>String\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>Ok(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">hi\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>.to_string())\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u002F\u002F 返回了错误信息error\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-style:italic\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">find\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>-&gt; Result\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>String,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>String\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>Err(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">error\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>.to_string())\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>那么有另外一个问题，就是，我们如何处理其他库或者第三方函数抛出给我们的错误呢？我们可以用\u003Ccode class=\"inline-code\">expect()\u003C\u002Fcode>,\u003Ccode class=\"inline-code\">unwrap()\u003C\u002Fcode>来解决可以解决的错误，但是有时候我们不希望处理错误，希望能够将错误抛出给上层，让上层调用者去处理。这时候，我们应该怎么定义错误类型？你当然可以在你的函数中进行错误处理之后，抛出\u003Ccode class=\"inline-code\">String\u003C\u002Fcode>类型的错误，以便继续使用\u003Ccode class=\"inline-code\">String\u003C\u002Fcode>类型作为错误类型。\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">#[\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">test\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">]\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">test_find\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>x\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>foo();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>println!\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>(\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">{:?}\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>x.unwrap())\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">foo\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>-&gt; Result\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>File,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>String\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>x\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>bar();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">match\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>x\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>Err(e)\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>Err(e.to_string()),\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>Ok(f)\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>Ok(f),\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">..\u003C\u002Fspan>.\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">bar\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>-&gt; Result\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>File,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>io::Error\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>File::open(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">regions.txt\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>)\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>你可以看到，我在\u003Ccode class=\"inline-code\">foo()\u003C\u002Fcode>中调用\u003Ccode class=\"inline-code\">bar()\u003C\u002Fcode>，\u003Ccode class=\"inline-code\">bar()\u003C\u002Fcode>中返回了一个包含\u003Ccode class=\"inline-code\">io::Error\u003C\u002Fcode>的错误，而我的\u003Ccode class=\"inline-code\">foo()\u003C\u002Fcode>中要求返回的是\u003Ccode class=\"inline-code\">String\u003C\u002Fcode>，我在\u003Ccode class=\"inline-code\">foo\u003C\u002Fcode>中用\u003Ccode class=\"inline-code\">match\u003C\u002Fcode>处理了错误的情况，并且\u003Ccode class=\"inline-code\">foo\u003C\u002Fcode>中可能调用其他第三方库函数，返回的错误类型不尽相同，我每种错误类型都可以使用\u003Ccode class=\"inline-code\">match\u003C\u002Fcode>解开，然后返回\u003Ccode class=\"inline-code\">String\u003C\u002Fcode>类型的错误。但是这样处理起来，代码看起来就非常乱，到处都是\u003Ccode class=\"inline-code\">match\u003C\u002Fcode>错误。\u003C\u002Fp>\n\n\u003Cp>其实我们有另一种方法，可以自己定义一个\u003Ccode class=\"inline-code\">Error\u003C\u002Fcode>类型，然后对\u003Ccode class=\"inline-code\">Error\u003C\u002Fcode>类型进行扩展，让它兼容其他类型，如下\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#ff7b72\">use\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>std::{fmt,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>io};\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">#[\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">derive(Debug)\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">]\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">pub\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">enum\u003C\u002Fspan> \u003Cspan style=\"color:#f0883e;font-weight:bold\">Error\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>ParseError,\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>ReadError,\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>InvalidIPError,\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">impl\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>std::error::Error\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">for\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>Error\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">impl\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>fmt::Display\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">for\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>Error\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">fmt\u003C\u002Fspan>(\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&amp;\u003C\u002Fspan>self,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>f: \u003Cspan style=\"color:#79c0ff\">&amp;\u003C\u002Fspan>\u003Cspan style=\"color:#f0883e;font-weight:bold\">mut\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>fmt::Formatter)\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>-&gt; \u003Cspan style=\"color:#f0883e;font-weight:bold\">fmt\u003C\u002Fspan>::Result\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">match\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>self\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">            \u003C\u002Fspan>Error::ParseError\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>write!\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>(f,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">Parse Error\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>),\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">            \u003C\u002Fspan>Error::ReadError\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>write!\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>(f,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">Read Error\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>),\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">            \u003C\u002Fspan>Error::InvalidIPError\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>write!\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>(f,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">Invalid IP Error\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>),\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">impl\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>From\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>io::Error\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">for\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>Error\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">from\u003C\u002Fspan>(_: \u003Cspan style=\"color:#f0883e;font-weight:bold\">io\u003C\u002Fspan>::Error)\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>-&gt; \u003Cspan style=\"color:#f0883e;font-weight:bold\">Self\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>Error::ReadError\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">impl\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>From\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>bincode::Error\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">for\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>Error\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">from\u003C\u002Fspan>(_: \u003Cspan style=\"color:#f0883e;font-weight:bold\">bincode\u003C\u002Fspan>::Error)\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>-&gt; \u003Cspan style=\"color:#f0883e;font-weight:bold\">Self\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>Error::ParseError\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">impl\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>From\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>std::net::AddrParseError\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">for\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>Error\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">from\u003C\u002Fspan>(_: \u003Cspan style=\"color:#f0883e;font-weight:bold\">std\u003C\u002Fspan>::net::AddrParseError)\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>-&gt; \u003Cspan style=\"color:#f0883e;font-weight:bold\">Self\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">        \u003C\u002Fspan>Error::InvalidIPError\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>我们的\u003Ccode class=\"inline-code\">Error\u003C\u002Fcode>类型，实现了\u003Ccode class=\"inline-code\">fmt::Display\u003C\u002Fcode>，让它能够处理我的包项目中可能出现的第三方错误，并且针对这些错误实现了各自的\u003Ccode class=\"inline-code\">From&lt;T&gt;\u003C\u002Fcode>的\u003Ccode class=\"inline-code\">from\u003C\u002Fcode>方法，这样，这些第三方错误就可以直接以我的\u003Ccode class=\"inline-code\">Error\u003C\u002Fcode>返回，使用?简写之后，代码就改成这样了\u003C\u002Fp>\n\n\u003Cpre style=\"color:#e6edf3;background-color:#0d1117;white-space:pre-wrap;word-break:break-word;\">\u003Ccode>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">#[\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">test\u003C\u002Fspan>\u003Cspan style=\"color:#8b949e;font-weight:bold;font-style:italic\">]\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">test_find\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>x\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>foo();\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>println!\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>(\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">{:?}\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>x.unwrap())\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">foo\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>-&gt; Result\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>File,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>Error\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">let\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>x\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72;font-weight:bold\">=\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>bar()\u003Cspan style=\"color:#ff7b72;font-weight:bold\">?\u003C\u002Fspan>;\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>Ok(x)\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#ff7b72\">fn\u003C\u002Fspan> \u003Cspan style=\"color:#d2a8ff;font-weight:bold\">bar\u003C\u002Fspan>()\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>-&gt; Result\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&lt;\u003C\u002Fspan>File,\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>io::Error\u003Cspan style=\"color:#ff7b72;font-weight:bold\">&gt;\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\"> \u003C\u002Fspan>{\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>\u003Cspan style=\"color:#6e7681\">    \u003C\u002Fspan>File::open(\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">regions.txt\u003C\u002Fspan>\u003Cspan style=\"color:#a5d6ff\">&#34;\u003C\u002Fspan>)\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003Cspan style=\"display:flex;\">\u003Cspan>\u003Cspan style=\"color:#6e7681\">\u003C\u002Fspan>}\u003Cspan style=\"color:#6e7681\">\n\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fspan>\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>对比可以看到，我在foo中处理bar抛出的错误变得简单了，直接一个问号就将可能得错误抛出到\u003Ccode class=\"inline-code\">Error\u003C\u002Fcode>了，然后再将\u003Ccode class=\"inline-code\">Ok(x)\u003C\u002Fcode>返回。这样一来，错误的抛出和处理就变得优雅多了。\u003C\u002Fp>\n",{"title":109,"list":110},"最热文章",[111,117,123,129,135,141,147,153,159,165],{"id":112,"title":113,"uri":114,"author":10,"created_at":115,"view_count":116},28,"docker容器目录与主机目录同步","\u002Farticle\u002Fdocker容器目录与主机目录同步","2014-12-31 12:34:08",15570,{"id":118,"title":119,"uri":120,"author":10,"created_at":121,"view_count":122},21,"beego中使用fis","\u002Farticle\u002Fbeego中使用fis","2014-12-03 10:47:56",9250,{"id":124,"title":125,"uri":126,"author":10,"created_at":127,"view_count":128},20,"用github登录，oauth开发","\u002Farticle\u002F用github登录，oauth开发","2014-11-23 13:33:37",9113,{"id":130,"title":131,"uri":132,"author":10,"created_at":133,"view_count":134},18,"windows服务注册工具nssm","\u002Farticle\u002Fwindows服务注册工具nssm","2014-10-31 21:34:39",9047,{"id":136,"title":137,"uri":138,"author":10,"created_at":139,"view_count":140},49,"用latex排版中文书籍","\u002Farticle\u002F用latex排版中文书籍","2016-10-31 11:12:02",8634,{"id":142,"title":143,"uri":144,"author":10,"created_at":145,"view_count":146},19,"texlive中文书签乱码","\u002Farticle\u002Ftexlive中文书签乱码","2014-11-15 02:54:01",8461,{"id":148,"title":149,"uri":150,"author":10,"created_at":151,"view_count":152},6,"Windows上的软件包管理器chocolatey","\u002Farticle\u002FWindows上的软件包管理器chocolatey","2014-08-10 15:55:22",8009,{"id":154,"title":155,"uri":156,"author":10,"created_at":157,"view_count":158},3,"利用开源和免费资源搭建CI环境","\u002Farticle\u002F利用开源和免费资源搭建CI环境","2014-07-04 16:54:58",7732,{"id":160,"title":161,"uri":162,"author":10,"created_at":163,"view_count":164},16,"php-fpm网站目录权限配置","\u002Farticle\u002Fphp-fpm网站目录权限配置","2014-10-18 01:36:10",7274,{"id":166,"title":167,"uri":168,"author":10,"created_at":169,"view_count":170},27,"sublime text 2插件开发，外部修改文件无法重新加载","\u002Farticle\u002Fsublime text 2插件开发，外部修改文件无法重新加载","2014-12-26 20:24:13",7054,{"title":172,"list":173},"按月归档",[174,178,181,184,187,190,193,197,201,204,207,210,214,217,220,224,227,230,233,237,240,243,246,249,252,255,258,261,264,267,270,273,276,279,282,285,288,291,294,297,301,304,307,310,313,316],{"title":175,"uri":176,"view_count":177},"2026年7月","\u002Farchive\u002F2026\u002F7\u002F1","[1]",{"title":179,"uri":180,"view_count":177},"2026年1月","\u002Farchive\u002F2026\u002F1\u002F1",{"title":182,"uri":183,"view_count":177},"2025年4月","\u002Farchive\u002F2025\u002F4\u002F1",{"title":185,"uri":186,"view_count":177},"2024年6月","\u002Farchive\u002F2024\u002F6\u002F1",{"title":188,"uri":189,"view_count":177},"2024年5月","\u002Farchive\u002F2024\u002F5\u002F1",{"title":191,"uri":192,"view_count":177},"2024年4月","\u002Farchive\u002F2024\u002F4\u002F1",{"title":194,"uri":195,"view_count":196},"2023年12月","\u002Farchive\u002F2023\u002F12\u002F1","[6]",{"title":198,"uri":199,"view_count":200},"2023年11月","\u002Farchive\u002F2023\u002F11\u002F1","[2]",{"title":202,"uri":203,"view_count":177},"2023年10月","\u002Farchive\u002F2023\u002F10\u002F1",{"title":205,"uri":206,"view_count":177},"2023年7月","\u002Farchive\u002F2023\u002F7\u002F1",{"title":208,"uri":209,"view_count":200},"2023年6月","\u002Farchive\u002F2023\u002F6\u002F1",{"title":211,"uri":212,"view_count":213},"2023年5月","\u002Farchive\u002F2023\u002F5\u002F1","[3]",{"title":215,"uri":216,"view_count":200},"2023年4月","\u002Farchive\u002F2023\u002F4\u002F1",{"title":218,"uri":219,"view_count":200},"2023年3月","\u002Farchive\u002F2023\u002F3\u002F1",{"title":221,"uri":222,"view_count":223},"2023年2月","\u002Farchive\u002F2023\u002F2\u002F1","[5]",{"title":225,"uri":226,"view_count":177},"2023年1月","\u002Farchive\u002F2023\u002F1\u002F1",{"title":228,"uri":229,"view_count":200},"2022年12月","\u002Farchive\u002F2022\u002F12\u002F1",{"title":231,"uri":232,"view_count":223},"2022年11月","\u002Farchive\u002F2022\u002F11\u002F1",{"title":234,"uri":235,"view_count":236},"2022年10月","\u002Farchive\u002F2022\u002F10\u002F1","[4]",{"title":238,"uri":239,"view_count":196},"2022年9月","\u002Farchive\u002F2022\u002F9\u002F1",{"title":241,"uri":242,"view_count":223},"2022年8月","\u002Farchive\u002F2022\u002F8\u002F1",{"title":244,"uri":245,"view_count":177},"2019年7月","\u002Farchive\u002F2019\u002F7\u002F1",{"title":247,"uri":248,"view_count":177},"2019年2月","\u002Farchive\u002F2019\u002F2\u002F1",{"title":250,"uri":251,"view_count":177},"2018年9月","\u002Farchive\u002F2018\u002F9\u002F1",{"title":253,"uri":254,"view_count":177},"2018年8月","\u002Farchive\u002F2018\u002F8\u002F1",{"title":256,"uri":257,"view_count":177},"2017年11月","\u002Farchive\u002F2017\u002F11\u002F1",{"title":259,"uri":260,"view_count":177},"2017年8月","\u002Farchive\u002F2017\u002F8\u002F1",{"title":262,"uri":263,"view_count":177},"2017年5月","\u002Farchive\u002F2017\u002F5\u002F1",{"title":265,"uri":266,"view_count":177},"2017年4月","\u002Farchive\u002F2017\u002F4\u002F1",{"title":268,"uri":269,"view_count":177},"2016年11月","\u002Farchive\u002F2016\u002F11\u002F1",{"title":271,"uri":272,"view_count":177},"2016年10月","\u002Farchive\u002F2016\u002F10\u002F1",{"title":274,"uri":275,"view_count":200},"2016年9月","\u002Farchive\u002F2016\u002F9\u002F1",{"title":277,"uri":278,"view_count":177},"2016年8月","\u002Farchive\u002F2016\u002F8\u002F1",{"title":280,"uri":281,"view_count":200},"2016年7月","\u002Farchive\u002F2016\u002F7\u002F1",{"title":283,"uri":284,"view_count":223},"2016年6月","\u002Farchive\u002F2016\u002F6\u002F1",{"title":286,"uri":287,"view_count":177},"2015年12月","\u002Farchive\u002F2015\u002F12\u002F1",{"title":289,"uri":290,"view_count":177},"2015年10月","\u002Farchive\u002F2015\u002F10\u002F1",{"title":292,"uri":293,"view_count":200},"2015年2月","\u002Farchive\u002F2015\u002F2\u002F1",{"title":295,"uri":296,"view_count":177},"2015年1月","\u002Farchive\u002F2015\u002F1\u002F1",{"title":298,"uri":299,"view_count":300},"2014年12月","\u002Farchive\u002F2014\u002F12\u002F1","[8]",{"title":302,"uri":303,"view_count":200},"2014年11月","\u002Farchive\u002F2014\u002F11\u002F1",{"title":305,"uri":306,"view_count":223},"2014年10月","\u002Farchive\u002F2014\u002F10\u002F1",{"title":308,"uri":309,"view_count":213},"2014年9月","\u002Farchive\u002F2014\u002F9\u002F1",{"title":311,"uri":312,"view_count":213},"2014年8月","\u002Farchive\u002F2014\u002F8\u002F1",{"title":314,"uri":315,"view_count":213},"2014年7月","\u002Farchive\u002F2014\u002F7\u002F1",{"title":317,"uri":318,"view_count":200},"2013年9月","\u002Farchive\u002F2013\u002F9\u002F1",104,{"url":321,"title":322},"https:\u002F\u002Fimage.duguying.net\u002F2022\u002F09\u002Fij6x8o4s.webp","老家 摄于2020年秋",1790062259618]