commit 5d520c9e33a6e29f884cafae176050f9244ce1b3 Author: sunfei Date: Tue Mar 19 10:08:38 2024 +0800 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fcbea52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +book +.DS_Store \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..c15d1da --- /dev/null +++ b/LICENSE @@ -0,0 +1,395 @@ +Attribution 4.0 International + +======================================================================= + +Creative Commons Corporation ("Creative Commons") is not a law firm and +does not provide legal services or legal advice. Distribution of +Creative Commons public licenses does not create a lawyer-client or +other relationship. Creative Commons makes its licenses and related +information available on an "as-is" basis. Creative Commons gives no +warranties regarding its licenses, any material licensed under their +terms and conditions, or any related information. Creative Commons +disclaims all liability for damages resulting from their use to the +fullest extent possible. + +Using Creative Commons Public Licenses + +Creative Commons public licenses provide a standard set of terms and +conditions that creators and other rights holders may use to share +original works of authorship and other material subject to copyright +and certain other rights specified in the public license below. The +following considerations are for informational purposes only, are not +exhaustive, and do not form part of our licenses. + + Considerations for licensors: Our public licenses are + intended for use by those authorized to give the public + permission to use material in ways otherwise restricted by + copyright and certain other rights. Our licenses are + irrevocable. Licensors should read and understand the terms + and conditions of the license they choose before applying it. + Licensors should also secure all rights necessary before + applying our licenses so that the public can reuse the + material as expected. Licensors should clearly mark any + material not subject to the license. This includes other CC- + licensed material, or material used under an exception or + limitation to copyright. More considerations for licensors: + wiki.creativecommons.org/Considerations_for_licensors + + Considerations for the public: By using one of our public + licenses, a licensor grants the public permission to use the + licensed material under specified terms and conditions. If + the licensor's permission is not necessary for any reason--for + example, because of any applicable exception or limitation to + copyright--then that use is not regulated by the license. Our + licenses grant only permissions under copyright and certain + other rights that a licensor has authority to grant. Use of + the licensed material may still be restricted for other + reasons, including because others have copyright or other + rights in the material. A licensor may make special requests, + such as asking that all changes be marked or described. + Although not required by our licenses, you are encouraged to + respect those requests where reasonable. More_considerations + for the public: + wiki.creativecommons.org/Considerations_for_licensees + +======================================================================= + +Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the "Licensor." The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b7ae2da --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +

Rusty Book( 锈书 )

+ +
+ +
+ +
+ + +在线阅读: https://rusty.course.rs +
+ +在 Rust 元宇宙,夸奖别人的最高境界就是 `rusty`: 今天你"锈"了吗? 你的 Rust 代码好锈啊!而本书,就是精选了各种开源库和代码片段,帮助大家打造优"锈"的 Rust 项目。 + +总之,如果有以下需求,那看锈书就对了: + +- 想要知道现在优秀的、关注度高的 Rust 项目有哪些 + +- 发现一些好玩、有趣、酷炫的开源库 + +- 需要寻找某个类型的库,例如,一个 HTTP 客户端或 ProtoBuffer 编码库,要求是好用、更新活跃、高质量 + +- 想要寻找常用操作的代码片段,用于熟悉 Rust 或者直接复制粘贴到自己的项目中,例如文件操作、数据库操作、HTTP 请求、排序算法、正则等 + + +细心的同学可能会发现,1-3 对应的是 [awesome-rust](https://github.com/rust-unofficial/awesome-rust), 4 对应的是 [rust-cookbook](https://github.com/rust-lang-nursery/rust-cookbook),那么锈书除了整合两块内容,合二为一外,还有其它的优点吗? + +## 它们存在的问题 + +**awesome-rust,最大的问题就是里面的内容鱼龙混**。低质量的、几年不更新的、分类不准的比比皆是,而且缺乏对项目的详细分析介绍,更多的就是把所有东西给你列出来,告诉你:诺,这几十个都是你想要的,自己挑吧。至于为什么会这样,根源在于它允许任何人去提交自己的库的链接,几乎没有做任何筛选。 + +所以,个人觉得不应该叫 awesome-rust,叫 all-rust 更为合适。如果存在疑义的同学,可以自己调研一番,再跟锈书的项目对比下,就明白我所言是否有虚了。 + + +对于开发者而言,Cookbook 非常实用,几乎每一门编程语言都是如此。原因无他:聪明的开发者大部分时间不是在复制粘贴就是在复制粘贴的路上。而 CookBook 恰恰为各种实用场景提供了可供直接复制粘贴的代码,例如网络协议、数据库和文件操作、随机数生成、命令行解析等。 + +**但目前的 Rust Cookbook 更新非常不活跃,里面缺少了大量实用库,还有一些过时的老库**。 + +哦对了,还有一点,它们都不是中文的,而锈书是基于中文写的,很快还将翻译成英文。 + +## 我们的优势 + +既然列出了别人的缺点,那我们自然是为了解决这些问题而来的。总的来说,锈书有以下特点: + +- 分类更加清晰、实用,也更方便于用户找到想要的库 + +- 所有的库都是精选的,就算通过 PR 提交的,也必须经过我们的严格审核后,再能加入 + +- 长期不更新的库( 一些已经稳定的工具库除外 )要坚决的移除 + +- 每一个库都有较为详细的介绍甚至是配图,让大家争取不用进到项目里调查一番,就知道该项目的详细用途 + +## 社区贡献 + +欢迎大家提交任何类型的 PR:好的库、实用的代码片段、内容和文字勘误等等,一切的一切对我们来说都特别宝贵,对于贡献高的用户,我们还将在未来送上神秘礼物和惊喜。 + + + + + diff --git a/assets/CNAME b/assets/CNAME new file mode 100644 index 0000000..58e26fe --- /dev/null +++ b/assets/CNAME @@ -0,0 +1 @@ +rusty.rs \ No newline at end of file diff --git a/assets/banner.gif b/assets/banner.gif new file mode 100644 index 0000000..c432bc8 Binary files /dev/null and b/assets/banner.gif differ diff --git a/assets/banner1.png b/assets/banner1.png new file mode 100644 index 0000000..5f024e5 Binary files /dev/null and b/assets/banner1.png differ diff --git a/assets/bigPicture.js b/assets/bigPicture.js new file mode 100644 index 0000000..c5063bd --- /dev/null +++ b/assets/bigPicture.js @@ -0,0 +1 @@ +var BigPicture=function(){var t,n,e,o,i,r,a,c,p,s,l,d,u,f,m,b,g,h,x,v,y,w,_,T,k,M,S,L,E,A,H,z,I,C=[],D={},O="appendChild",N="createElement",V="removeChild";function W(){var n=t.getBoundingClientRect();return"transform:translate3D("+(n.left-(e.clientWidth-n.width)/2)+"px, "+(n.top-(e.clientHeight-n.height)/2)+"px, 0) scale3D("+t.clientWidth/o.clientWidth+", "+t.clientHeight/o.clientHeight+", 0)"}function q(t){var n=A.length-1;if(!u){if(t>0&&E===n||t<0&&!E){if(!I.loop)return j(i,""),void setTimeout(j,9,i,"animation:"+(t>0?"bpl":"bpf")+" .3s;transition:transform .35s");E=t>0?-1:n+1}if([(E=Math.max(0,Math.min(E+t,n)))-1,E,E+1].forEach(function(t){if(t=Math.max(0,Math.min(t,n)),!D[t]){var e=A[t].src,o=document[N]("IMG");o.addEventListener("load",F.bind(null,e)),o.src=e,D[t]=o}}),D[E].complete)return B(t);u=1,j(m,"opacity:.4;"),e[O](m),D[E].onload=function(){y&&B(t)},D[E].onerror=function(){A[E]={error:"Error loading image"},y&&B(t)}}}function B(n){u&&(e[V](m),u=0);var r=A[E];if(r.error)alert(r.error);else{var a=e.querySelector("img:last-of-type");j(i=o=D[E],"animation:"+(n>0?"bpfl":"bpfr")+" .35s;transition:transform .35s"),j(a,"animation:"+(n>0?"bpfol":"bpfor")+" .35s both"),e[O](i),r.el&&(t=r.el)}H.innerHTML=E+1+"/"+A.length,X(A[E].caption),M&&M([i,A[E]])}function P(){var t,n,e=.95*window.innerHeight,o=.95*window.innerWidth,i=I.dimensions||[1920,1080],r=i[0],a=i[1],p=a/r;p>e/o?n=(t=Math.min(a,e))/p:t=(n=Math.min(r,o))*p,c.style.cssText+="width:"+n+"px;height:"+t+"px;"}function G(t){~[1,4].indexOf(o.readyState)?(U(),setTimeout(function(){o.play()},99)):o.error?U(t):f=setTimeout(G,35,t)}function R(n){I.noLoader||(n&&j(m,"top:"+t.offsetTop+"px;left:"+t.offsetLeft+"px;height:"+t.clientHeight+"px;width:"+t.clientWidth+"px"),t.parentElement[n?O:V](m),u=n)}function X(t){t&&(g.innerHTML=t),j(b,"opacity:"+(t?"1;pointer-events:auto":"0"))}function F(t){!~C.indexOf(t)&&C.push(t)}function U(t){if(u&&R(),T&&T(),"string"==typeof t)return $(),I.onError?I.onError():alert("Error: The requested "+t+" could not be loaded.");_&&F(s),o.style.cssText+=W(),j(e,"opacity:1;pointer-events:auto"),k=setTimeout(k,410),v=1,y=!!A,setTimeout(function(){o.style.cssText+="transition:transform .35s;transform:none",h&&setTimeout(X,250,h)},60)}function Y(t){var n=t?t.target:e,i=[b,x,r,a,g,L,S,m];n.blur(),w||~i.indexOf(n)||(o.style.cssText+=W(),j(e,"pointer-events:auto"),setTimeout($,350),clearTimeout(k),v=0,w=1)}function $(){if((o===c?p:o).removeAttribute("src"),document.body[V](e),e[V](o),j(e,""),j(o,""),X(0),y){for(var t=e.querySelectorAll("img"),n=0;n',n}function d(t,n){var e=document[N]("button");return e.className="bp-lr",e.innerHTML='',j(e,n),e.onclick=function(n){n.stopPropagation(),q(t)},e}var f=document[N]("STYLE");f.innerHTML="#bp_caption,#bp_container{bottom:0;left:0;right:0;position:fixed;opacity:0}#bp_container>*,#bp_loader{position:absolute;right:0;z-index:10}#bp_container,#bp_caption,#bp_container svg{pointer-events:none}#bp_container{top:0;z-index:9999;background:rgba(0,0,0,.7);opacity:0;transition:opacity .35s}#bp_loader{top:0;left:0;bottom:0;display:flex;align-items:center;cursor:wait;background:0;z-index:9}#bp_loader svg{width:50%;max-width:300px;max-height:50%;margin:auto;animation:bpturn 1s infinite linear}#bp_aud,#bp_container img,#bp_sv,#bp_vid{user-select:none;max-height:96%;max-width:96%;top:0;bottom:0;left:0;margin:auto;box-shadow:0 0 3em rgba(0,0,0,.4);z-index:-1}#bp_sv{background:#111}#bp_sv svg{width:66px}#bp_caption{font-size:.9em;padding:1.3em;background:rgba(15,15,15,.94);color:#fff;text-align:center;transition:opacity .3s}#bp_aud{width:650px;top:calc(50% - 20px);bottom:auto;box-shadow:none}#bp_count{left:0;right:auto;padding:14px;color:rgba(255,255,255,.7);font-size:22px;cursor:default}#bp_container button{position:absolute;border:0;outline:0;background:0;cursor:pointer;transition:all .1s}#bp_container>.bp-x{padding:0;height:41px;width:41px;border-radius:100%;top:8px;right:14px;opacity:.8;line-height:1}#bp_container>.bp-x:focus,#bp_container>.bp-x:hover{background:rgba(255,255,255,.2)}.bp-x svg,.bp-xc svg{height:21px;width:20px;fill:#fff;vertical-align:top;}.bp-xc svg{width:16px}#bp_container .bp-xc{left:2%;bottom:100%;padding:9px 20px 7px;background:#d04444;border-radius:2px 2px 0 0;opacity:.85}#bp_container .bp-xc:focus,#bp_container .bp-xc:hover{opacity:1}.bp-lr{top:50%;top:calc(50% - 130px);padding:99px 0;width:6%;background:0;border:0;opacity:.4;transition:opacity .1s}.bp-lr:focus,.bp-lr:hover{opacity:.8}@keyframes bpf{50%{transform:translatex(15px)}100%{transform:none}}@keyframes bpl{50%{transform:translatex(-15px)}100%{transform:none}}@keyframes bpfl{0%{opacity:0;transform:translatex(70px)}100%{opacity:1;transform:none}}@keyframes bpfr{0%{opacity:0;transform:translatex(-70px)}100%{opacity:1;transform:none}}@keyframes bpfol{0%{opacity:1;transform:none}100%{opacity:0;transform:translatex(-70px)}}@keyframes bpfor{0%{opacity:1;transform:none}100%{opacity:0;transform:translatex(70px)}}@keyframes bpturn{0%{transform:none}100%{transform:rotate(360deg)}}@media (max-width:600px){.bp-lr{font-size:15vw}}",document.head[O](f),(e=document[N]("DIV")).id="bp_container",e.onclick=Y,l=s("bp-x"),e[O](l),"ontouchstart"in window&&(z=1,e.ontouchstart=function(n){var e=n.changedTouches;t=e[0].pageX},e.ontouchmove=function(t){t.preventDefault()},e.ontouchend=function(n){var e=n.changedTouches;if(y){var o=e[0].pageX-t;o<-30&&q(1),o>30&&q(-1)}}),i=document[N]("IMG"),(r=document[N]("VIDEO")).id="bp_vid",r.setAttribute("playsinline",1),r.controls=1,r.loop=1,(a=document[N]("audio")).id="bp_aud",a.controls=1,a.loop=1,(H=document[N]("span")).id="bp_count",(b=document[N]("DIV")).id="bp_caption",(x=s("bp-xc")).onclick=X.bind(null,0),b[O](x),g=document[N]("SPAN"),b[O](g),e[O](b),S=d(1,"transform:scalex(-1)"),L=d(-1,"left:0;right:auto"),(m=document[N]("DIV")).id="bp_loader",m.innerHTML='',(c=document[N]("DIV")).id="bp_sv",(p=document[N]("IFRAME")).setAttribute("allowfullscreen",1),p.allow="autoplay; fullscreen",p.onload=function(){return c[V](m)},j(p,"border:0;position:absolute;height:100%;width:100%;left:0;top:0"),c[O](p),i.onload=U,i.onerror=U.bind(null,"image"),window.addEventListener("resize",function(){y||u&&R(1),o===c&&P()}),document.addEventListener("keyup",function(t){var n=t.keyCode;27===n&&v&&Y(),y&&(39===n&&q(1),37===n&&q(-1),38===n&&q(10),40===n&&q(-10))}),document.addEventListener("keydown",function(t){y&&~[37,38,39,40].indexOf(t.keyCode)&&t.preventDefault()}),document.addEventListener("focus",function(t){v&&!e.contains(t.target)&&(t.stopPropagation(),l.focus())},1),n=1}(),u&&(clearTimeout(f),$()),I=w,d=w.ytSrc||w.vimeoSrc,T=w.animationStart,k=w.animationEnd,M=w.onChangeImage,_=0,h=(t=w.el).getAttribute("data-caption"),w.gallery?function(n,r){var a=I.galleryAttribute||"data-bp";if(Array.isArray(n))A=n,h=n[E=r||0].caption;else{var c=(A=[].slice.call("string"==typeof n?document.querySelectorAll(n+" ["+a+"]"):n)).indexOf(t);E=0===r||r?r:-1!==c?c:0,A=A.map(function(t){return{el:t,src:t.getAttribute(a),caption:t.getAttribute("data-caption")}})}_=1,!~C.indexOf(s=A[E].src)&&R(1),A.length>1?(e[O](H),H.innerHTML=E+1+"/"+A.length,z||(e[O](S),e[O](L))):A=0,(o=i).src=s}(w.gallery,w.position):d||w.iframeSrc?(o=c,I.ytSrc?W="https://www.youtube.com/embed/"+d+"?html5=1&rel=0&playsinline=1&autoplay=1":I.vimeoSrc?W="https://player.vimeo.com/video/"+d+"?autoplay=1":I.iframeSrc&&(W=I.iframeSrc),j(m,""),c[O](m),p.src=W,P(),setTimeout(U,9)):w.imgSrc?(_=1,!~C.indexOf(s=w.imgSrc)&&R(1),(o=i).src=s):w.audio?(R(1),(o=a).src=w.audio,G("audio file")):w.vidSrc?(R(1),w.dimensions&&j(r,"width:"+w.dimensions[0]+"px"),D=w.vidSrc,Array.isArray(D)?(o=r.cloneNode(),D.forEach(function(t){var n=document[N]("SOURCE");n.src=t,n.type="video/"+t.match(/.(\w+)$/)[1],o[O](n)})):(o=r).src=D,G("video")):(o=i).src="IMG"===t.tagName?t.src:window.getComputedStyle(t).backgroundImage.replace(/^url|[(|)|'|"]/g,""),e[O](o),document.body[O](e),{close:Y,next:function(){return q(1)},prev:function(){return q(-1)}};var W}}(); \ No newline at end of file diff --git a/assets/custom1.js b/assets/custom1.js new file mode 100644 index 0000000..b064b88 --- /dev/null +++ b/assets/custom1.js @@ -0,0 +1,158 @@ +var initAll = function () { + var path = window.location.pathname; + if (path.endsWith("/print.html")) { + return; + } + + var images = document.querySelectorAll("main img") + Array.prototype.forEach.call(images, function (img) { + img.addEventListener("click", function () { + BigPicture({ + el: img, + }); + }); + }); + + // Un-active everything when you click it + Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) { + el.addEventHandler("click", function () { + Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) { + el.classList.remove("active"); + }); + el.classList.add("active"); + }); + }); + + var updateFunction = function () { + var id = null; + var elements = document.getElementsByClassName("header"); + Array.prototype.forEach.call(elements, function (el) { + if (window.pageYOffset >= el.offsetTop) { + id = el; + } + }); + + Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) { + el.classList.remove("active"); + }); + + Array.prototype.forEach.call(document.getElementsByClassName("pagetoc")[0].children, function (el) { + if (id == null) { + return; + } + if (id.href.localeCompare(el.href) == 0) { + el.classList.add("active"); + } + }); + }; + + var pagetoc = document.getElementsByClassName("pagetoc")[0]; + var elements = document.getElementsByClassName("header"); + Array.prototype.forEach.call(elements, function (el) { + var link = document.createElement("a"); + + // Indent shows hierarchy + var indent = ""; + switch (el.parentElement.tagName) { + case "H1": + return; + case "H3": + indent = "20px"; + break; + case "H4": + indent = "40px"; + break; + default: + break; + } + + link.appendChild(document.createTextNode(el.text)); + link.style.paddingLeft = indent; + link.href = el.href; + pagetoc.appendChild(link); + }); + updateFunction.call(); + + // Handle active elements on scroll + window.addEventListener("scroll", updateFunction); + + document.getElementById("theme-list").addEventListener("click", function (e) { + var iframe = document.querySelector('.giscus-frame'); + if (!iframe) return; + var theme; + if (e.target.className === "theme") { + theme = e.target.id; + } else { + return; + } + + // 若当前 mdbook 主题不是 Light 或 Rust ,则将 giscuz 主题设置为 transparent_dark + var giscusTheme = "light" + if (theme != "light" && theme != "rust") { + giscusTheme = "transparent_dark"; + } + + var msg = { + setConfig: { + theme: giscusTheme + } + }; + iframe.contentWindow.postMessage({ giscus: msg }, 'https://giscus.app'); + }); + + pagePath = pagePath.replace("index.md", ""); + pagePath = pagePath.replace(".md", ""); + if (pagePath.length > 0) { + if (pagePath.charAt(pagePath.length-1) == "/"){ + pagePath = pagePath.substring(0, pagePath.length-1) + } + }else { + pagePath = "index" + } + + // add vistors count + var ele = document.createElement("div"); + ele.setAttribute("align","center"); + var count = document.createElement("img") + count.setAttribute("src", "https://visitor-badge.glitch.me/badge?page_id=" + path); + ele.appendChild(count); + var divider =document.createElement("hr") + + document.getElementById("giscus-container").appendChild(ele); + document.getElementById("giscus-container").appendChild(divider); + + // 选取浏览器默认使用的语言 + const lang = navigator.language || navigator.userLanguage + + // 若当前 mdbook 主题为 Light 或 Rust ,则将 giscuz 主题设置为 light + var theme = "transparent_dark"; + const themeClass = document.getElementsByTagName("html")[0].className; + if (themeClass.indexOf("light") != -1 || themeClass.indexOf("rust") != -1) { + theme = "light" + } + + var script = document.createElement("script") + script.type = "text/javascript"; + script.src = "https://giscus.app/client.js"; + script.async = true; + script.crossOrigin = "anonymous"; + script.setAttribute("data-repo", "studyrs/rusty-book"); + script.setAttribute("data-repo-id", "R_kgDOGmKA_Q"); + script.setAttribute("data-category", "giscus"); + script.setAttribute("data-category-id", "DIC_kwDOHH0skM4COa8c"); + script.setAttribute("data-mapping", "specific"); + script.setAttribute("data-term", pagePath); + script.setAttribute("data-reactions-enabled", "1"); + script.setAttribute("data-emit-metadata", "0"); + script.setAttribute("data-input-position", "top"); + script.setAttribute("data-theme", theme); + script.setAttribute("data-lang", lang); + // 预先加载评论会更好,这样用户读到那边时,评论就加载好了 + // script.setAttribute("data-loading", "lazy"); + document.getElementById("giscus-container").appendChild(script); + + + +}; + +window.addEventListener('load', initAll); \ No newline at end of file diff --git a/book.toml b/book.toml new file mode 100644 index 0000000..5040fe2 --- /dev/null +++ b/book.toml @@ -0,0 +1,24 @@ +[book] +authors = ["sunface"] +language = "zh-CN" +title = "Rusty Book(锈书)" +src = "src" + +[output.html] +no-section-label = true +additional-css = ["theme/style1.css"] +additional-js = ["assets/custom1.js", "assets/bigPicture.js"] +git-repository-url = "https://github.com/studyrs/rusty-book" +edit-url-template = "https://github.com/studyrs/rusty-book/edit/main/{path}" + +[output.html.playground] +editable = true +copy-js = true +# line-numbers = true + +[output.html.fold] +enable = true +level = 1 + +[rust] +edition = "2021" #在线运行用2021版本的 diff --git a/deploy b/deploy new file mode 100755 index 0000000..236ff7b --- /dev/null +++ b/deploy @@ -0,0 +1,20 @@ +## this script deploys the static website of course.rs to github pages + +## build static website for book +mdbook build +## copy CNAME info to book dir +cp ./assets/CNAME ./book/ + + +## init git repo +cd book +git init +git config user.name "sunface" +git config user.email "cto@188.com" +git add . +git commit -m 'deploy' +git branch -M gh-pages +git remote add origin https://github.com/rustlang-cn/new-rusty-book + +## push to github pages +git push -u -f origin gh-pages diff --git a/src/SUMMARY.md b/src/SUMMARY.md new file mode 100644 index 0000000..ca7e468 --- /dev/null +++ b/src/SUMMARY.md @@ -0,0 +1,89 @@ +# Summary + +[Rusty Book](about.md) + + + + +# Awesome +--- +- [日常开发常用库](awesome-daily-dev.md) +- [Rust 明星项目](awesome-superstar.md) +- [使用 Rust 增强 JS](awesome-empowering-js.md) +- [Rust开发的游戏](awesome-games.md) +- [游戏引擎](awesome-gamedev.md) + + +# Awesome + Cookbook +--- +- [实用算法](algos/awesome.md) + - [数据结构]() + - [位字段](algos/datastructures/bitfield.md) + - [生成随机值](algos/randomness.md) + - [Vec 排序](algos/sorting.md) + - [压缩算法]() + - [使用.tar包](algos/compression/tar.md) + - [密码学]() + - [哈希](algos/cryptography/hashing.md) + - [加密](algos/cryptography/encryption.md) + - [数学计算]() + - [线性代数](algos/math/linear-algebra.md) + - [三角函数](algos/math/trigonometry.md) + - [复数](algos/math/complex.md) + - [统计学](algos/math/statistics.md) + - [杂项](algos/math/misc.md) + +- [命令行](cmd/awesome.md) + - [参数解析](cmd/parsing.md) + - [终端输出格式化](cmd/ansi.md) + +- [操作系统](os/awesome.md) + - [处理器](os/processor.md) + - [调用系统命令](os/command.md) + +- [并发和并行]() + - [多线程](cocurrency/threads.md) + - [使用rayon并行处理数据](cocurrency/parallel.md) + +- [数据库]() + - [SQLite](database/sqlite.md) + - [Postgres](database/postgres.md) + + +- [日期和时间]() + - [时间计算和转换](datetime/duration.md) + - [解析和显示](datetime/parsing.md) + + +- [开发者工具]() + - [日志](devtools/log.md) + - [配置日志](devtools/config-log.md) + - [版本号](devtools/version.md) + - [构建时工具](devtools/build-tools.md) + + +- [编解码]() + - [字符编码](encoding/strings.md) + - [CSV](encoding/csv.md) + - [结构化数据](encoding/structured.md) + + +- [错误处理]() + +- [文件操作]() + - [文件读写](files/read-write.md) + - [目录访问](files/dir.md) + +- [内存管理]() + - [全局变量](memory/global-vars.md) + +- [网络协议]() + - [TCP/IP](protocols/tcpip.md) + +- [文本处理]() + - [正则表达式](text/regex.md) + - [字符串解析](text/string.md) + +- [Web编程]() + - [提取网络链接( 爬虫 )](web/scraping.md) \ No newline at end of file diff --git a/src/about.md b/src/about.md new file mode 100644 index 0000000..01cbe66 --- /dev/null +++ b/src/about.md @@ -0,0 +1,41 @@ + + +每个同学可能都遇到过以下疑惑: + +- 学完 Rust 后,还做了些题,接下可以做些什么? +- 需要找一个依赖,但是去哪里找?哪些比较好用?哪些有坑?愁啊 +- 要访问一个文件,哎,但记不住代码,要不百度或谷歌一下吧,最后发现结果往往不尽如人意 + +而 Rusty Book 就是帮助大家解决这些问题的。 + +在 Rust 元宇宙,夸奖别人的最高境界就是 `rusty`: 今天你"锈"了吗? 你的 Rust 代码好锈啊! + +而本书,就是精选了各种开源库和代码片段,帮助大家打造优"锈"的 Rust 项目。 + +## 以往的锈 +以往,想要锈起来,你需要做到以下两步: + +1. 为项目挑选 Awesome 依赖库 +但是目前已有的 awesome-rust项目有非常大的问题:里面鱼龙混杂,因为它的目的是列出所有项目,但对用户而言,更想看到的是可以在生产中使用的、稳定更新的优秀项目。 + +2. 在 Cookbook 中查询实用的代码片段,直接复制到项目中 +对于开发者而言,Cookbook 非常实用,几乎每一门编程语言都是如此。原因无他:聪明的开发者大部分时间不是在复制粘贴就是在复制粘贴的路上。而 CookBook 恰恰为各种实用场景提供了可供直接复制粘贴的代码,例如网络协议、数据库和文件操作、随机数生成、命令行解析等。 + +但目前的 Rust Cookbook 更新非常不活跃,里面缺少了大量实用库,还有一些过时的老库。 + +## 现在的锈 +鉴于以上痛点,我们决定打造一本真正的锈书:一本足够"锈"但是又不会锈的书。 + +这本书其实就是 Awesome Rust + Rust Cookbook 的结合体,但是我们不是简单粗暴的对内容进行了合并,而是从深层次将两者进行了融合,希望大家能喜欢。 + +## 这本书的读者 +本书适合所有程度的 Rust 开发者使用: + +- 新手用来了解 Rust 的常用库和常用代码片段 +- 老手在写代码时,可以直接用来复制粘贴,大幅提升工作效率 + +毕竟咱不是在面试造飞机,谁脑袋中能记住文件操作的各种细节,对不? + + + + diff --git a/src/algos/awesome.md b/src/algos/awesome.md new file mode 100644 index 0000000..d0f3f1e --- /dev/null +++ b/src/algos/awesome.md @@ -0,0 +1,37 @@ +# Awesome 算法 + +## 通用算法 +--- +### rust-algorithms + +[rust-algorithms](https://github.com/EbTech/rust-algorithms)收集了一些经典的算法和数据结构,更强调算法实现的美观性,因此该库更适用于教学目的,请不要把它当成一个实用算法库在生产环境使用。 + +### TheAlgorithms/Rust +[TheAlgorithms/Rust](https://github.com/TheAlgorithms/Rust)项目所属的[组织](https://github.com/TheAlgorithms)使用各种语言实现了多种算法,但是仅适用于演示的目的。 + +## Leetcode +--- + +### rustgym +[rustgym](https://github.com/warycat/rustgym) 实现了相当多的 leetcode 和 Avent of Code 题解。 + +## 分布式算法 +--- + +### raft-rs + +[raft-rs](https://github.com/tikv/raft-rs) 是由 Tikv 提供的 Raft 分布式算法实现。[Raft](https://raft.github.io)是一个强一致性的分布式算法,比 Paxos 协议更简单、更好理解 + +## 密码学 +--- + +### Rust Crypto + +[Rust Crypto](https://github.com/RustCrypto)提供了一些常用的密码学算法实现,更新较为活跃。 + +## 专用算法 +--- + +### rust-bio + +[rust-bio](https://github.com/rust-bio/rust-bio) 有常用的生物信息学所需的算法和数据结构。 \ No newline at end of file diff --git a/src/algos/compression/tar.md b/src/algos/compression/tar.md new file mode 100644 index 0000000..992d8dd --- /dev/null +++ b/src/algos/compression/tar.md @@ -0,0 +1,77 @@ +# 使用tar包 + +## 解压 tar 包 +以下代码将解压缩( [GzDecoder](https://docs.rs/flate2/*/flate2/read/struct.GzDecoder.html) )当前目录中的 `archive.tar.gz` ,并将所有文件抽取出( [Archive::unpack](https://docs.rs/tar/*/tar/struct.Archive.html#method.unpack) )来后当入到当前目录中。 + +```rust,editable +use std::fs::File; +use flate2::read::GzDecoder; +use tar::Archive; + +fn main() -> Result<(), std::io::Error> { + let path = "archive.tar.gz"; + + let tar_gz = File::open(path)?; + let tar = GzDecoder::new(tar_gz); + let mut archive = Archive::new(tar); + archive.unpack(".")?; + + Ok(()) +} +``` + +## 将目录压缩成 tar 包 +以下代码将 `/var/log` 目录压缩成 `archive.tar.gz`: + +- 创建一个 [File](https://doc.rust-lang.org/std/fs/struct.File.html) 文件,并使用 [GzEncoder](https://docs.rs/flate2/*/flate2/write/struct.GzEncoder.html) 和 [tar::Builder](https://docs.rs/tar/*/tar/struct.Builder.html) 对其进行包裹 +- 通过 [Builder::append_dir_all](https://docs.rs/tar/*/tar/struct.Builder.html#method.append_dir_all) 将 `/var/log` 目录下的所有内容添加到压缩文件中,该文件在 `backup/logs` 目录下。 +- [GzEncoder](https://docs.rs/flate2/*/flate2/write/struct.GzEncoder.html) 负责在写入压缩文件 `archive.tar.gz` 之前对数据进行压缩。 + +```rust,editable +use std::fs::File; +use flate2::Compression; +use flate2::write::GzEncoder; + +fn main() -> Result<(), std::io::Error> { + let tar_gz = File::create("archive.tar.gz")?; + let enc = GzEncoder::new(tar_gz, Compression::default()); + let mut tar = tar::Builder::new(enc); + tar.append_dir_all("backup/logs", "/var/log")?; + Ok(()) +} +``` + +## 解压的同时删除指定的文件前缀 +遍历目录中的文件 [Archive::entries](https://docs.rs/tar/*/tar/struct.Archive.html#method.entries),若解压前的文件名包含 `bundle/logs` 前缀,需要将前缀从文件名移除( [Path::strip_prefix](https://doc.rust-lang.org/std/path/struct.Path.html#method.strip_prefix) )后,再解压。 + + + +```rust,editable +use std::fs::File; +use std::path::PathBuf; +use flate2::read::GzDecoder; +use tar::Archive; + +fn main() -> Result<()> { + let file = File::open("archive.tar.gz")?; + let mut archive = Archive::new(GzDecoder::new(file)); + let prefix = "bundle/logs"; + + println!("Extracted the following files:"); + archive + .entries()? // 获取压缩档案中的文件条目列表 + .filter_map(|e| e.ok()) + // 对每个文件条目进行 map 处理 + .map(|mut entry| -> Result { + // 将文件路径名中的前缀移除,获取一个新的路径名 + let path = entry.path()?.strip_prefix(prefix)?.to_owned(); + // 将内容解压到新的路径名中 + entry.unpack(&path)?; + Ok(path) + }) + .filter_map(|e| e.ok()) + .for_each(|x| println!("> {}", x.display())); + + Ok(()) +} +``` \ No newline at end of file diff --git a/src/algos/cryptography/encryption.md b/src/algos/cryptography/encryption.md new file mode 100644 index 0000000..7e354b7 --- /dev/null +++ b/src/algos/cryptography/encryption.md @@ -0,0 +1,57 @@ +# 加密 + +### 使用 PBKDF2 对密码进行哈希和加盐( salt ) +[ring::pbkdf2](https://briansmith.org/rustdoc/ring/pbkdf2/index.html) 可以对一个加盐密码进行哈希。 + +```rust,editable + +use data_encoding::HEXUPPER; +use ring::error::Unspecified; +use ring::rand::SecureRandom; +use ring::{digest, pbkdf2, rand}; +use std::num::NonZeroU32; + +fn main() -> Result<(), Unspecified> { + const CREDENTIAL_LEN: usize = digest::SHA512_OUTPUT_LEN; + let n_iter = NonZeroU32::new(100_000).unwrap(); + let rng = rand::SystemRandom::new(); + + let mut salt = [0u8; CREDENTIAL_LEN]; + // 生成 salt: 将安全生成的随机数填入到字节数组中 + rng.fill(&mut salt)?; + + let password = "Guess Me If You Can!"; + let mut pbkdf2_hash = [0u8; CREDENTIAL_LEN]; + pbkdf2::derive( + pbkdf2::PBKDF2_HMAC_SHA512, + n_iter, + &salt, + password.as_bytes(), + &mut pbkdf2_hash, + ); + println!("Salt: {}", HEXUPPER.encode(&salt)); + println!("PBKDF2 hash: {}", HEXUPPER.encode(&pbkdf2_hash)); + + // `verify` 检查哈希是否正确 + let should_succeed = pbkdf2::verify( + pbkdf2::PBKDF2_HMAC_SHA512, + n_iter, + &salt, + password.as_bytes(), + &pbkdf2_hash, + ); + let wrong_password = "Definitely not the correct password"; + let should_fail = pbkdf2::verify( + pbkdf2::PBKDF2_HMAC_SHA512, + n_iter, + &salt, + wrong_password.as_bytes(), + &pbkdf2_hash, + ); + + assert!(should_succeed.is_ok()); + assert!(!should_fail.is_ok()); + + Ok(()) +} +``` diff --git a/src/algos/cryptography/hashing.md b/src/algos/cryptography/hashing.md new file mode 100644 index 0000000..60a697c --- /dev/null +++ b/src/algos/cryptography/hashing.md @@ -0,0 +1,71 @@ +# 哈希 + +### 计算文件的 SHA-256 摘要 +写入一些数据到文件中,然后使用 [digest::Context](https://briansmith.org/rustdoc/ring/digest/struct.Context.html) 来计算文件内容的 SHA-256 摘要 [digest::Digest](https://briansmith.org/rustdoc/ring/digest/struct.Digest.html)。 + +```rust,editable +# use error_chain::error_chain; +use data_encoding::HEXUPPER; +use ring::digest::{Context, Digest, SHA256}; +use std::fs::File; +use std::io::{BufReader, Read, Write}; + +# error_chain! { +# foreign_links { +# Io(std::io::Error); +# Decode(data_encoding::DecodeError); +# } +# } + +fn sha256_digest(mut reader: R) -> Result { + let mut context = Context::new(&SHA256); + let mut buffer = [0; 1024]; + + loop { + let count = reader.read(&mut buffer)?; + if count == 0 { + break; + } + context.update(&buffer[..count]); + } + + Ok(context.finish()) +} + +fn main() -> Result<()> { + let path = "file.txt"; + + let mut output = File::create(path)?; + write!(output, "We will generate a digest of this text")?; + + let input = File::open(path)?; + let reader = BufReader::new(input); + let digest = sha256_digest(reader)?; + + println!("SHA-256 digest is {}", HEXUPPER.encode(digest.as_ref())); + + Ok(()) +} +``` + +### 使用 HMAC 摘要来签名和验证消息 +使用 [ring::hmac](https://briansmith.org/rustdoc/ring/hmac/) 创建一个字符串签名并检查该签名的正确性。 + +```rust,editable +use ring::{hmac, rand}; +use ring::rand::SecureRandom; +use ring::error::Unspecified; + +fn main() -> Result<(), Unspecified> { + let mut key_value = [0u8; 48]; + let rng = rand::SystemRandom::new(); + rng.fill(&mut key_value)?; + let key = hmac::Key::new(hmac::HMAC_SHA256, &key_value); + + let message = "Legitimate and important message."; + let signature = hmac::sign(&key, message.as_bytes()); + hmac::verify(&key, message.as_bytes(), signature.as_ref())?; + + Ok(()) +} +``` \ No newline at end of file diff --git a/src/algos/datastructures/awesome.md b/src/algos/datastructures/awesome.md new file mode 100644 index 0000000..c12ddc5 --- /dev/null +++ b/src/algos/datastructures/awesome.md @@ -0,0 +1,2 @@ +# 数据结构 + diff --git a/src/algos/datastructures/bitfield.md b/src/algos/datastructures/bitfield.md new file mode 100644 index 0000000..4c9c29e --- /dev/null +++ b/src/algos/datastructures/bitfield.md @@ -0,0 +1,47 @@ +# 位字段 + +### 定义和操作位字段 +使用 [`bitflags!`](https://docs.rs/bitflags/1.3.2/bitflags/macro.bitflags.html) 宏可以帮助我们创建安全的位字段类型 `MyFlags`,然后为其实现基本的 `clear` 操作。以下代码展示了基本的位操作和格式化: +```rust,editable +use bitflags::bitflags; +use std::fmt; + +bitflags! { + struct MyFlags: u32 { + const FLAG_A = 0b00000001; + const FLAG_B = 0b00000010; + const FLAG_C = 0b00000100; + const FLAG_ABC = Self::FLAG_A.bits + | Self::FLAG_B.bits + | Self::FLAG_C.bits; + } +} + +impl MyFlags { + pub fn clear(&mut self) -> &mut MyFlags { + self.bits = 0; + self + } +} + +impl fmt::Display for MyFlags { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:032b}", self.bits) + } +} + +fn main() { + let e1 = MyFlags::FLAG_A | MyFlags::FLAG_C; + let e2 = MyFlags::FLAG_B | MyFlags::FLAG_C; + assert_eq!((e1 | e2), MyFlags::FLAG_ABC); + assert_eq!((e1 & e2), MyFlags::FLAG_C); + assert_eq!((e1 - e2), MyFlags::FLAG_A); + assert_eq!(!e2, MyFlags::FLAG_A); + + let mut flags = MyFlags::FLAG_ABC; + assert_eq!(format!("{}", flags), "00000000000000000000000000000111"); + assert_eq!(format!("{}", flags.clear()), "00000000000000000000000000000000"); + assert_eq!(format!("{:?}", MyFlags::FLAG_B), "FLAG_B"); + assert_eq!(format!("{:?}", MyFlags::FLAG_A | MyFlags::FLAG_B), "FLAG_A | FLAG_B"); +} +``` \ No newline at end of file diff --git a/src/algos/math/complex.md b/src/algos/math/complex.md new file mode 100644 index 0000000..89c5854 --- /dev/null +++ b/src/algos/math/complex.md @@ -0,0 +1,43 @@ +# 复数 + +### 创建复数 +[num::complex::Complex](https://autumnai.github.io/cuticula/num/complex/struct.Complex.html) 可以帮助我们创建复数,其中实部和虚部必须是一样的类型。 + +```rust,editable +fn main() { + let complex_integer = num::complex::Complex::new(10, 20); + let complex_float = num::complex::Complex::new(10.1, 20.1); + + println!("Complex integer: {}", complex_integer); + println!("Complex float: {}", complex_float); +} +``` + +### 复数相加 +复数计算和 Rust 基本类型的计算并无区别。 + +```rust,editable +fn main() { + let complex_num1 = num::complex::Complex::new(10.0, 20.0); // Must use floats + let complex_num2 = num::complex::Complex::new(3.1, -4.2); + + let sum = complex_num1 + complex_num2; + + println!("Sum: {}", sum); +} +``` + +### 数学函数 +在 [num::complex::Complex](https://autumnai.github.io/cuticula/num/complex/struct.Complex.html) 中定义了一些内置的数学函数,可用于对复数进行数学运算。 + +```rust,editable +use std::f64::consts::PI; +use num::complex::Complex; + +fn main() { + let x = Complex::new(0.0, 2.0*PI); + + println!("e^(2i * pi) = {}", x.exp()); // =~1 +} +``` + diff --git a/src/algos/math/linear-algebra.md b/src/algos/math/linear-algebra.md new file mode 100644 index 0000000..fc361ec --- /dev/null +++ b/src/algos/math/linear-algebra.md @@ -0,0 +1,176 @@ +# 线性代数 + +### 矩阵相加 + +使用 [ndarray::arr2](https://docs.rs/ndarray/*/ndarray/fn.arr2.html) 可以创建二阶矩阵,并计算它们的和。 + +```rust,editable +use ndarray::arr2; + +fn main() { + let a = arr2(&[[1, 2, 3], + [4, 5, 6]]); + + let b = arr2(&[[6, 5, 4], + [3, 2, 1]]); + + // 借用 a 和 b,求和后生成新的矩阵 sum + let sum = &a + &b; + + println!("{}", a); + println!("+"); + println!("{}", b); + println!("="); + println!("{}", sum); +} +``` + +### 矩阵相乘 + +[ndarray::ArrayBase::dot](https://docs.rs/ndarray/0.15.4/ndarray/struct.ArrayBase.html#method.dot-1) 可以用于计算矩阵乘法。 + +```rust,editable +use ndarray::arr2; + +fn main() { + let a = arr2(&[[1, 2, 3], + [4, 5, 6]]); + + let b = arr2(&[[6, 3], + [5, 2], + [4, 1]]); + + println!("{}", a.dot(&b)); +} +``` + +### 标量、向量、矩阵相乘 + +在 `ndarry`中,1 阶数组根据上下文既可以作为行向量也可以作为列向量。如果对你来说,这个行或列的方向很重要,可以考虑使用一行或一列的 2 阶数组来表示。 + +在下面例子中,由于 1 阶数组处于乘号的右边位置,因此 `dot` 会把它当成列向量来处理。 + +```rust,editable +use ndarray::{arr1, arr2, Array1}; + +fn main() { + let scalar = 4; + + let vector = arr1(&[1, 2, 3]); + + let matrix = arr2(&[[4, 5, 6], + [7, 8, 9]]); + + let new_vector: Array1<_> = scalar * vector; + println!("{}", new_vector); + + let new_matrix = matrix.dot(&new_vector); + println!("{}", new_matrix); +} +``` + +### 向量比较 + +浮点数通常是不精确的,因此比较浮点数不是一件简单的事。[approx](https://docs.rs/approx/*/approx/index.html) 提供的 [assert_abs_diff_eq!](https://docs.rs/approx/0.5.1/approx/macro.assert_abs_diff_eq.html) 宏提供了方便的按元素比较的方式。为了使用 `approx` ,你需要在 `ndarray` 的依赖中开启相应的 feature:例如,在 `Cargo.toml` 中修改 `ndarray` 的依赖引入为 `ndarray = { version = "0.13", features = ["approx"] }`。 + +```rust,editable +use approx::assert_abs_diff_eq; +use ndarray::Array; + +fn main() { + let a = Array::from(vec![1., 2., 3., 4., 5.]); + let b = Array::from(vec![5., 4., 3., 2., 1.]); + let mut c = Array::from(vec![1., 2., 3., 4., 5.]); + let mut d = Array::from(vec![5., 4., 3., 2., 1.]); + + // 消耗 a 和 b 的所有权 + let z = a + b; + // 借用 c 和 d + let w = &c + &d; + + assert_abs_diff_eq!(z, Array::from(vec![6., 6., 6., 6., 6.])); + + println!("c = {}", c); + c[0] = 10.; + d[1] = 10.; + + assert_abs_diff_eq!(w, Array::from(vec![6., 6., 6., 6., 6.])); + +} +``` + +### 向量范数( norm ) + +需要注意的是 `Array` 和 `ArrayView` 都是 `ArrayBase` 的别名。因此一个更通用的参数应该是 `&ArrayBase where S: Data`,特别是在你提供一个公共 API 给其它用户时,但由于咱们是内部使用,因此更精准的 `ArrayView1` 会更适合。 + +```rust,editable +use ndarray::{array, Array1, ArrayView1}; + +fn l1_norm(x: ArrayView1) -> f64 { + x.fold(0., |acc, elem| acc + elem.abs()) +} + +fn l2_norm(x: ArrayView1) -> f64 { + x.dot(&x).sqrt() +} + +fn normalize(mut x: Array1) -> Array1 { + let norm = l2_norm(x.view()); + x.mapv_inplace(|e| e/norm); + x +} + +fn main() { + let x = array![1., 2., 3., 4., 5.]; + println!("||x||_2 = {}", l2_norm(x.view())); + println!("||x||_1 = {}", l1_norm(x.view())); + println!("Normalizing x yields {:?}", normalize(x)); +} +``` + +### 矩阵的逆变换 +例子中使用 [nalgebra::Matrix3](https://docs.rs/nalgebra/*/nalgebra/base/type.Matrix3.html) 创建一个 3x3 的矩阵,然后尝试对其进行逆变换,获取一个逆矩阵。 + +```rust,editable +use nalgebra::Matrix3; + +fn main() { + let m1 = Matrix3::new(2.0, 1.0, 1.0, 3.0, 2.0, 1.0, 2.0, 1.0, 2.0); + println!("m1 = {}", m1); + match m1.try_inverse() { + Some(inv) => { + println!("The inverse of m1 is: {}", inv); + } + None => { + println!("m1 is not invertible!"); + } + } +} +``` + +### 序列/反序列化一个矩阵 + +下面将展示如何将矩阵序列化为 JSON ,然后再反序列化为原矩阵。 + +```rust,editable +extern crate nalgebra; +extern crate serde_json; + +use nalgebra::DMatrix; + +fn main() -> Result<(), std::io::Error> { + let row_slice: Vec = (1..5001).collect(); + let matrix = DMatrix::from_row_slice(50, 100, &row_slice); + + // 序列化矩阵 + let serialized_matrix = serde_json::to_string(&matrix)?; + + // 反序列化 + let deserialized_matrix: DMatrix = serde_json::from_str(&serialized_matrix)?; + + // 验证反序列化后的矩阵跟原始矩阵相等 + assert!(deserialized_matrix == matrix); + + Ok(()) +} +``` \ No newline at end of file diff --git a/src/algos/math/misc.md b/src/algos/math/misc.md new file mode 100644 index 0000000..d438a22 --- /dev/null +++ b/src/algos/math/misc.md @@ -0,0 +1,24 @@ +# 杂项 + +### 大整数 Big int +使用 [BitInt](https://docs.rs/num/0.2.0/num/struct.BigInt.html) 可以对超过 128bit 的整数进行计算。 + +```rust,editable +use num::bigint::{BigInt, ToBigInt}; + +fn factorial(x: i32) -> BigInt { + if let Some(mut factorial) = 1.to_bigint() { + for i in 1..=x { + factorial = factorial * i; + } + factorial + } + else { + panic!("Failed to calculate factorial!"); + } +} + +fn main() { + println!("{}! equals {}", 100, factorial(100)); +} +``` \ No newline at end of file diff --git a/src/algos/math/statistics.md b/src/algos/math/statistics.md new file mode 100644 index 0000000..05e92f6 --- /dev/null +++ b/src/algos/math/statistics.md @@ -0,0 +1,176 @@ +# 统计 + +### 测量中心趋势 + +下面的一些例子为 Rust 数组中的数据计算它们的中心趋势。 + +#### 平均值 +首先计算的是平均值。 + +```rust,editable +fn main() { + let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11]; + + let sum = data.iter().sum::() as f32; + let count = data.len(); + + let mean = match count { + positive if positive > 0 => Some(sum / count as f32), + _ => None + }; + + println!("Mean of the data is {:?}", mean); +} +``` + +#### 中位数 +下面使用快速选择算法来计算中位数。该算法只会对可能包含中位数的数据分区进行排序,从而避免了对所有数据进行全排序。 + +```rust,editable +use std::cmp::Ordering; + +fn partition(data: &[i32]) -> Option<(Vec, i32, Vec)> { + match data.len() { + 0 => None, + _ => { + let (pivot_slice, tail) = data.split_at(1); + let pivot = pivot_slice[0]; + let (left, right) = tail.iter() + .fold((vec![], vec![]), |mut splits, next| { + { + let (ref mut left, ref mut right) = &mut splits; + if next < &pivot { + left.push(*next); + } else { + right.push(*next); + } + } + splits + }); + + Some((left, pivot, right)) + } + } +} + +fn select(data: &[i32], k: usize) -> Option { + let part = partition(data); + + match part { + None => None, + Some((left, pivot, right)) => { + let pivot_idx = left.len(); + + match pivot_idx.cmp(&k) { + Ordering::Equal => Some(pivot), + Ordering::Greater => select(&left, k), + Ordering::Less => select(&right, k - (pivot_idx + 1)), + } + }, + } +} + +fn median(data: &[i32]) -> Option { + let size = data.len(); + + match size { + even if even % 2 == 0 => { + let fst_med = select(data, (even / 2) - 1); + let snd_med = select(data, even / 2); + + match (fst_med, snd_med) { + (Some(fst), Some(snd)) => Some((fst + snd) as f32 / 2.0), + _ => None + } + }, + odd => select(data, odd / 2).map(|x| x as f32) + } +} + +fn main() { + let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11]; + + let part = partition(&data); + println!("Partition is {:?}", part); + + let sel = select(&data, 5); + println!("Selection at ordered index {} is {:?}", 5, sel); + + let med = median(&data); + println!("Median is {:?}", med); +} +``` + +#### 众数( mode ) +下面使用了 `HashMap` 对不同数字出现的次数进行了分别统计。 + +```rust,editable +use std::collections::HashMap; + +fn main() { + let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11]; + + let frequencies = data.iter().fold(HashMap::new(), |mut freqs, value| { + *freqs.entry(value).or_insert(0) += 1; + freqs + }); + + let mode = frequencies + .into_iter() + .max_by_key(|&(_, count)| count) + .map(|(value, _)| *value); + + println!("Mode of the data is {:?}", mode); +} +``` + +### 标准偏差 + +下面一起来看看该如何计算一组测量值的标准偏差和 z-score。 + +```rust,editable +fn mean(data: &[i32]) -> Option { + let sum = data.iter().sum::() as f32; + let count = data.len(); + + match count { + positive if positive > 0 => Some(sum / count as f32), + _ => None, + } +} + +fn std_deviation(data: &[i32]) -> Option { + match (mean(data), data.len()) { + (Some(data_mean), count) if count > 0 => { + let variance = data.iter().map(|value| { + let diff = data_mean - (*value as f32); + + diff * diff + }).sum::() / count as f32; + + Some(variance.sqrt()) + }, + _ => None + } +} + +fn main() { + let data = [3, 1, 6, 1, 5, 8, 1, 8, 10, 11]; + + let data_mean = mean(&data); + println!("Mean is {:?}", data_mean); + + let data_std_deviation = std_deviation(&data); + println!("Standard deviation is {:?}", data_std_deviation); + + let zscore = match (data_mean, data_std_deviation) { + (Some(mean), Some(std_deviation)) => { + let diff = data[4] as f32 - mean; + + Some(diff / std_deviation) + }, + _ => None + }; + println!("Z-score of data at index 4 (with value {}) is {:?}", data[4], zscore); +} +``` \ No newline at end of file diff --git a/src/algos/math/trigonometry.md b/src/algos/math/trigonometry.md new file mode 100644 index 0000000..fae094f --- /dev/null +++ b/src/algos/math/trigonometry.md @@ -0,0 +1,59 @@ +# 三角函数 + +### 三角形边长计算 +计算角为 2 弧度、对边长度为 80 的直角三角形的斜边长度。 + +```rust,editable +fn main() { + let angle: f64 = 2.0; + let side_length = 80.0; + + let hypotenuse = side_length / angle.sin(); + + println!("Hypotenuse: {}", hypotenuse); +} +``` + +### 验证 tan = sin / cos + +```rust,editable +fn main() { + let x: f64 = 6.0; + + let a = x.tan(); + let b = x.sin() / x.cos(); + + assert_eq!(a, b); +} +``` + +### 地球上两点间的距离 +下面的代码使用 [Haversine 公式](https://blog.csdn.net/Hardict/article/details/105267473) 计算地球上两点之间的公里数。 + +```rust,editable +fn main() { + let earth_radius_kilometer = 6371.0_f64; + let (paris_latitude_degrees, paris_longitude_degrees) = (48.85341_f64, -2.34880_f64); + let (london_latitude_degrees, london_longitude_degrees) = (51.50853_f64, -0.12574_f64); + + let paris_latitude = paris_latitude_degrees.to_radians(); + let london_latitude = london_latitude_degrees.to_radians(); + + let delta_latitude = (paris_latitude_degrees - london_latitude_degrees).to_radians(); + let delta_longitude = (paris_longitude_degrees - london_longitude_degrees).to_radians(); + + let central_angle_inner = (delta_latitude / 2.0).sin().powi(2) + + paris_latitude.cos() * london_latitude.cos() * (delta_longitude / 2.0).sin().powi(2); + let central_angle = 2.0 * central_angle_inner.sqrt().asin(); + + let distance = earth_radius_kilometer * central_angle; + + println!( + "Distance between Paris and London on the surface of Earth is {:.1} kilometers", + distance + ); +} +``` + + + diff --git a/src/algos/randomness.md b/src/algos/randomness.md new file mode 100644 index 0000000..04241a7 --- /dev/null +++ b/src/algos/randomness.md @@ -0,0 +1,155 @@ +# 生成随机值 + +### 生成随机数 + +使用 [rand::thread_rng](https://docs.rs/rand/*/rand/fn.thread_rng.html) 可以获取一个随机数生成器 [rand::Rng](https://docs.rs/rand/0.8.5/rand/trait.Rng.html) ,该生成器需要在每个线程都初始化一个。 + +整数的随机分布范围等于类型的取值范围,但是浮点数只分布在 `[0, 1)` 区间内。 + +```rust,editable +use rand::Rng; + +fn main() { + let mut rng = rand::thread_rng(); + + let n1: u8 = rng.gen(); + let n2: u16 = rng.gen(); + println!("Random u8: {}", n1); + println!("Random u16: {}", n2); + println!("Random u32: {}", rng.gen::()); + println!("Random i32: {}", rng.gen::()); + println!("Random float: {}", rng.gen::()); +} +``` + +### 指定范围生成随机数 + +使用 [Rng::gen_range](https://rust-lang-nursery.github.io/rust-cookbook/algorithms/randomness.html) 生成 [0, 10) 区间内的随机数( 右开区间,不包括 `10` )。 +```rust,editable +use rand::Rng; + +fn main() { + let mut rng = rand::thread_rng(); + println!("Integer: {}", rng.gen_range(0..10)); + println!("Float: {}", rng.gen_range(0.0..10.0)); +} +``` + +[Uniform](https://docs.rs/rand/*/rand/distributions/uniform/struct.Uniform.html) 可以用于生成均匀分布uniform distribution的随机数。当需要在同一个范围内重复生成随机数时,该方法虽然和之前的方法效果一样,但会更快一些。 + +```rust,editable +use rand::distributions::{Distribution, Uniform}; + +fn main() { + let mut rng = rand::thread_rng(); + let die = Uniform::from(1..7); + + loop { + let throw = die.sample(&mut rng); + println!("Roll the die: {}", throw); + if throw == 6 { + break; + } + } +} +``` + +### 使用指定分布来生成随机数 + +默认情况下,`rand` 包使用均匀分布来生成随机数,而 [rand_distr](https://docs.rs/rand_distr/*/rand_distr/index.html) 包提供了其它类型的分布方式。 + +首先,你需要获取想要使用的分布的实例,然后在 [rand::Rng](https://docs.rs/rand/*/rand/trait.Rng.html) 的帮助下使用 [Distribution::sample](https://docs.rs/rand/*/rand/distributions/trait.Distribution.html#tymethod.sample) 对该实例进行取样。 + +如果想要查询可用的分布列表,可以访问[这里](https://docs.rs/rand_distr/*/rand_distr/index.html),下面的示例中我们将使用 [Normal](https://docs.rs/rand_distr/0.4.3/rand_distr/struct.Normal.html) 分布: +```rust,editable +use rand_distr::{Distribution, Normal, NormalError}; +use rand::thread_rng; + +fn main() -> Result<(), NormalError> { + let mut rng = thread_rng(); + let normal = Normal::new(2.0, 3.0)?; + let v = normal.sample(&mut rng); + println!("{} is from a N(2, 9) distribution", v); + Ok(()) +} +``` + +### 在自定义类型中生成随机值 + + +使用 [Distribution](https://docs.rs/rand/*/rand/distributions/trait.Distribution.html) 特征包裹我们的自定义类型,并为 [Standard](https://docs.rs/rand/*/rand/distributions/struct.Standard.html) 实现该特征,可以为自定义类型的指定字段生成随机数。 + + +```rust,editable +use rand::Rng; +use rand::distributions::{Distribution, Standard}; + +#[derive(Debug)] +struct Point { + x: i32, + y: i32, +} + +impl Distribution for Standard { + fn sample(&self, rng: &mut R) -> Point { + let (rand_x, rand_y) = rng.gen(); + Point { + x: rand_x, + y: rand_y, + } + } +} + +fn main() { + let mut rng = rand::thread_rng(); + + // 生成一个随机的 Point + let rand_point: Point = rng.gen(); + println!("Random Point: {:?}", rand_point); + + // 通过类型暗示( hint )生成一个随机的元组 + let rand_tuple = rng.gen::<(i32, bool, f64)>(); + println!("Random tuple: {:?}", rand_tuple); +} +``` + +### 生成随机的字符串(A-Z, a-z, 0-9) +通过 [Alphanumeric](https://docs.rs/rand/0.8.5/rand/distributions/struct.Alphanumeric.html) 采样来生成随机的 ASCII 字符串,包含从 `A-Z, a-z, 0-9` 的字符。 + +```rust,editble +use rand::{thread_rng, Rng}; +use rand::distributions::Alphanumeric; + +fn main() { + let rand_string: String = thread_rng() + .sample_iter(&Alphanumeric) + .take(30) + .map(char::from) + .collect(); + + println!("{}", rand_string); +} +``` + +### 生成随机的字符串( 用户指定 ASCII 字符 ) +通过 [gen_string](https://docs.rs/rand/0.8.5/rand/trait.Rng.html#method.gen_range) 生成随机的 ASCII 字符串,包含用户指定的字符。 + +```rust,editable +fn main() { + use rand::Rng; + const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\ + abcdefghijklmnopqrstuvwxyz\ + 0123456789)(*&^%$#@!~"; + const PASSWORD_LEN: usize = 30; + let mut rng = rand::thread_rng(); + + let password: String = (0..PASSWORD_LEN) + .map(|_| { + let idx = rng.gen_range(0..CHARSET.len()); + CHARSET[idx] as char + }) + .collect(); + + println!("{:?}", password); +} +``` diff --git a/src/algos/sorting.md b/src/algos/sorting.md new file mode 100644 index 0000000..76e44e8 --- /dev/null +++ b/src/algos/sorting.md @@ -0,0 +1,84 @@ +## Vector 排序 + + +### 对整数 Vector 排序 + +以下示例使用 [Vec::sort](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort) 来排序,如果大家希望获得更高的性能,可以使用 [Vec::sort_unstable](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort_unstable),但是该方法无法保留相等元素的顺序。 + +```rust,editable +fn main() { + let mut vec = vec![1, 5, 10, 2, 15]; + + vec.sort(); + + assert_eq!(vec, vec![1, 2, 5, 10, 15]); +} +``` + +### 对浮点数 Vector 排序 + +浮点数数组可以使用 [Vec::sort_by](https://doc.rust-lang.org/std/primitive.slice.html#method.sort_by) 和 [PartialOrd::partial_cmp](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html#tymethod.partial_cmp) 进行排序。 + +```rust,editable +fn main() { + let mut vec = vec![1.1, 1.15, 5.5, 1.123, 2.0]; + + vec.sort_by(|a, b| a.partial_cmp(b).unwrap()); + + assert_eq!(vec, vec![1.1, 1.123, 1.15, 2.0, 5.5]); +} +``` + +### 对结构体 Vector 排序 + +以下示例中的结构体 `Person` 将实现基于字段 `name` 和 `age` 的自然排序。为了让 `Person` 变为可排序的,我们需要为其派生 `Eq、PartialEq、Ord、PartialOrd` 特征,关于这几个特征的详情,请见[这里](https://course.rs/advance/confonding/eq.html)。 + +当然,还可以使用 [vec:sort_by](https://doc.rust-lang.org/std/vec/struct.Vec.html#method.sort_by) 方法配合一个自定义比较函数,只按照 `age` 的维度对 `Person` 数组排序。 + +```rust,editable +#[derive(Debug, Eq, Ord, PartialEq, PartialOrd)] +struct Person { + name: String, + age: u32 +} + +impl Person { + pub fn new(name: String, age: u32) -> Self { + Person { + name, + age + } + } +} + +fn main() { + let mut people = vec![ + Person::new("Zoe".to_string(), 25), + Person::new("Al".to_string(), 60), + Person::new("John".to_string(), 1), + ]; + + // 通过派生后的自然顺序(Name and age)排序 + people.sort(); + + assert_eq!( + people, + vec![ + Person::new("Al".to_string(), 60), + Person::new("John".to_string(), 1), + Person::new("Zoe".to_string(), 25), + ]); + + // 只通过 age 排序 + people.sort_by(|a, b| b.age.cmp(&a.age)); + + assert_eq!( + people, + vec![ + Person::new("Al".to_string(), 60), + Person::new("Zoe".to_string(), 25), + Person::new("John".to_string(), 1), + ]); + +} +``` \ No newline at end of file diff --git a/src/awesome-daily-dev.md b/src/awesome-daily-dev.md new file mode 100644 index 0000000..c667770 --- /dev/null +++ b/src/awesome-daily-dev.md @@ -0,0 +1,166 @@ +# 日常开发常用库 + +### 目录索引 +- [多线程](#多线程) +- [Web/HTTP](#webhttp), [SQL客户端](#SQL客户端), [NoSql客户端](#NoSql客户端), [网络通信协议](#网络通信协议), [异步网络编程](#异步网络编程) +- [服务发现](#服务发现), [消息队列](#消息队列), [搜索引擎](#搜索引擎) +- [编解码](#编解码), [Email](#Email), [常用正则模版](#常用正则模版) +- [日志监控](#日志监控), [代码Debug](#代码Debug), [性能优化](#性能优化) + +### Web/HTTP +* HTTP客户端 + * [reqwest](https://github.com/seanmonstar/reqwest) 一个简单又强大的HTTP客户端,`reqwest`是目前使用最多的HTTP库 + +* Web框架 + * [axum](https://github.com/tokio-rs/axum) 基于Tokio和Hyper打造,模块化设计较好,目前口碑很好,值得使用Ergonomic and modular web framework built with Tokio, Tower, and Hyper + * [Rocket](https://github.com/SergioBenitez/Rocket) 功能强大,API简单的Web框架,但是主要开发者目前因为个人原因无法进行后续开发,未来存在不确定性 + * [actix-web](https://github.com/actix/actix-web) 性能极高的Web框架,就是团队内部有些问题,未来存在一定的不确定性 + * 总体来说,上述三个web框架都有很深的用户基础,其实都可以选用,如果让我推荐,顺序如下: `axum` > `Rocket` > `actix-web`。 不过如果你不需要多么完善的web功能,只需要一个性能极高的http库,那么`actix-web`是非常好的选择,它的性能非常非常非常高! + +### 日志监控 +* 日志 +[[crates.io](https://crates.io/keywords/log)] [[github](https://github.com/search?q=rust+log)] + * [tokio-rs/tracing](https://github.com/tokio-rs/tracing) 强大的日志框架,同时还支持OpenTelemetry格式,无缝打通未来的监控 + * [rust-lang/log](https://github.com/rust-lang/log) 官方日志库,事实上的API标准, 但是三方库未必遵循 + * [estk/log4rs](https://github.com/estk/log4rs) 模仿JAVA `logback`和`log4j`实现的日志库, 可配置性较强 + * 在其它文章中,也许会推荐slog,但是我们不推荐,一个是因为近半年未更新,一个是`slog`自己也推荐使用`tracing`。 +* 监控 + * [OpenTelemetry](https://github.com/open-telemetry/opentelemetry-rust) `OpenTelemetry`是现在非常火的可观测性解决方案,提供了协议、API、SDK等核心工具,用于收集监控数据,最后将这些metrics/logs/traces数据写入到`prometheus`, `jaeger`等监控平台中。最主要是,它后台很硬,后面有各大公司作为背书,未来非常看好! + * [vectordotdev/vector](https://github.com/vectordotdev/vector) 一个性能很高的数据采集agent,采集本地的日志、监控等数据,发送到远程的kafka、jaeger等数据下沉端,它最大的优点就是能从多种数据源(包括Opentelemetry)收集数据,然后推送到多个数据处理或者存储等下沉端。 + +### SQL客户端 +* 性能对比 + * [metrics](https://github.com/diesel-rs/metrics) 该库对Rust现存的数据库连接服务进行性能测试,若大家有性能上的需求,值得一看 + +* 通用 + * [launchbadge/sqlx](https://github.com/launchbadge/sqlx) 异步实现、高性能、纯Rust代码的SQL库,支持`PostgreSQL`, `MySQL`, `SQLite`,和 `MSSQL`. + +* ORM + * [rbatis/rbatis](https://github.com/rbatis/rbatis) 国内团队开发的ORM,异步、性能高、简单易上手 + * [diesel-rs/diesel](https://github.com/diesel-rs/diesel) 安全、扩展性强的Rust ORM库,支持`Mysql`、`Postgre`、`SqlLite` + + +* Mysql + * [blackbeam/rust-mysql-simple](https://github.com/blackbeam/rust-mysql-simple) 纯Rust实现的Mysql驱动,提供连接池 + * [blackbeam/mysql_async](https://github.com/blackbeam/mysql_async) 基于Tokio实现的异步Mysql驱动 + * 上面两个都是一个团队出品,前者文档更全、star更多,建议使用前者 + + +* Postgre + * [sfackler/rust-postgres](https://github.com/sfackler/rust-postgres) 纯Rust实现的Postgre客户端 + +* Sqlite + * [rusqlite](https://github.com/rusqlite/rusqlite) 用于[Sqlite3](https://www.sqlite.org/index.html)的Rust客户端 + +### NoSql客户端 + +* Redis + * [mitsuhiko/redis-rs](https://github.com/mitsuhiko/redis-rs) 虽然最近更新不太活跃,但是它依然是最好的redis客户端,说实话,我期待更好的,可能这也是Rust生态的未来可期之处吧 + +* Canssandra + * [krojew/cdrs-tokio](https://github.com/krojew/cdrs-tokio) [[cdrs-tokio](https://crates.io/crates/cdrs-tokio)] 生产可用的Cassandra客户端,异步、纯Rust实现,就是个人项目 + star较少,未来不确定会不会不维护 + * [scylla-rust-driver](https://github.com/scylladb/scylla-rust-driver) ScyllaDB提供的官方库,支持cql协议,由于背靠大山,未来非常可期 + + +* MongoDB + * [mongodb/mongo-rust-driver](https://github.com/mongodb/mongo-rust-driver) 官方MongoDB客户端,闭着眼睛选就对了 + + +### 分布式 +#### 服务发现 +- [luncj/etcd-rs](https://github.com/luncj/etcd-rs) 异步实现的Rust etcd客户端,优点是有一定的文档、作者较为活跃,意味着你提问题他可能会回答,不过,如果你不放心,还是考虑使用HTTP的方式访问ETCD + +#### 消息队列 +* Kafka + * [fede1024/rust-rdkafka](https://github.com/fede1024/rust-rdkafka) Rust Kafka客户端,基于C版本的Kafka库[librdkafka]实现,文档较全、功能较为全面 + * [kafka-rust/kafka-rust](https://github.com/kafka-rust/kafka-rust) 相比上一个库,它算是纯Rust实现,文档还行,支持Kafka0.8.2及以后的版本,但是对于部分0.9版本的特性还不支持。同时有一个问题:最初的作者不维护了,转给了现在的作者,但是感觉好像也不是很活跃 +* Nats + * [nats-io/nats.rs](https://github.com/nats-io/nats.rs) Nats官方提供的客户端 + +### 网络、通信协议 +* Websocket + * [snapview/tokio-tungstenite](https://github.com/snapview/tokio-tungstenite) 更适合Web应用使用的生产级Websocket库,它是异步非阻塞的,基于基于下下面的`tungstenite-rs`库和tokio实现 + * [rust-websocket](https://github.com/websockets-rs/rust-websocket) 老牌Websocket库,提供了客户端和服务器端实现,但是。。。很久没更新了 + * [snapview/tungstenite-rs](https://github.com/snapview/tungstenite-rs) 轻量级的Websocket流实现,该库更偏底层,例如,你可以用来构建其它网络库 +* gRPC + * [hyperium/tonic](https://github.com/hyperium/tonic) 纯Rust实现的gRPC客户端和服务器端,支持async/await异步调用,文档和示例较为清晰 + * [tikv/grpc-rs](https://github.com/tikv/grpc-rs) 国产开源之光Tidb团队出品的gRPC框架, 基于C的代码实现, 就是最近好像不是很活跃 + * 其实这两个实现都很优秀,把`tonic`放在第一位,主要是因为它是纯Rust实现,同时社区也更为活跃,但是并不代表它比`tikv`的更好! + * [tokio-rs/prost](https://github.com/tokio-rs/prost) 纯Rust实现的[Protocol Buffers](https://developers.google.com/protocol-buffers/)类库,Prost 支持从 proto2 和 proto3 文件生成简单、实用的代码。 +* QUIC + * [cloudflare/quiche](https://github.com/cloudflare/quiche) 大名鼎鼎`cloudflare`提供的QUIC实现,据说在公司内部重度使用,有了大规模生产级别的验证,非常值得信任,同时该库还实现了HTTP/3 + * [quinn-rs/quinn](https://github.com/quinn-rs/quinn) 提供异步API调用,纯Rust实现,同时提供了几个有用的网络库 +* MQTT + * [bytebeamio/rumqtt](https://github.com/bytebeamio/rumqtt) MQTT3.1.1/5协议库,同时实现了客户端与服务器端broker + * [ntex-rs/ntex-mqtt](https://github.com/ntex-rs/ntex-mqtt) 客户端与服务端框架,支持MQTT3.1.1与5协议 + * [eclipse/paho.mqtt.rust](https://github.com/eclipse/paho.mqtt.rust) 老牌MQTT框架,对MQTT支持较全, 其它各语言的实现也有 + +### 异步网络编程 + +* [tokio-rs/tokio](https://github.com/tokio-rs/tokio) 最火的异步网络库,除了复杂上手难度高一些外,没有其它大的问题。同时tokio团队提供了多个非常优秀的Rust库,整个生态欣欣向荣,用户认可度很高 +* [async-std](https://async.rs/) 跟标准库API很像的异步网络库,相对简单易用,但是貌似开发有些停滞,还有就是功能上不够完善。但是对于普通用户来说,这个库非常值得一试,它在功能和简单易用上取得了很好的平衡 +* [actix](https://github.com/actix/actix) 基于Actor模型的异步网络库,但这个库的开发貌似已经停滞,他们团队一直在专注于`actix-web`的开发 +* [mio](https://github.com/tokio-rs/mio) 严格来说,MIO与之前三个不是同一个用途的,MIO = Meta IO,是一个底层IO库,往往用于构建其它网络库,当然如果你对应用网络性能有非常极限的要求, 可以考虑它,因为它的层次比较低,所带来的抽象负担小,所以性能损耗小 +* 如果你要开发生产级别的项目,我推荐使用`tokio`,稳定可靠,功能丰富,控制粒度细;自己的学习项目或者没有那么严肃的开源项目,我推荐`async-std`,简单好用,值得学习;当你确切知道需要Actor网络模型时,就用`actix` + + +### 搜索引擎 + +* ElasticSearch客户端 + * [elastic/elasticsearch](https://github.com/elastic/elasticsearch-rs) 官方es客户端,目前第三方的基本都处于停滞状态,所以不管好坏,用呗 + +* Rust搜索引擎 + * [Tantivy](https://github.com/quickwit-inc/tantivy) Tantivy是Rust实现的本地搜索库,功能对标`lucene`,如果你不需要分布式,那么引入tantivy作为自己本地Rust服务的一个搜索,是相当不错的选择,该库作者一直很活跃,而且最近还创立了搜索引擎公司,感觉大有作为. 该库的优点在于纯Rust实现,性能高(lucene的2-3倍),资源占用低(对比java自然不是一个数量级),社区活跃。 + +* Rust搜索平台 + * [quickwit](https://github.com/quickwit-inc/quickwit) 对标ElasticSearch,一个通用目的的分布式搜索平台,目前还在起步阶段(0.2版本),未来非常可期,目前还不建议使用 + * [MeiliSearch](https://github.com/meilisearch/MeiliSearch) 虽然也是一个搜索平台,但是并不是通用目的的,`MeiliSearch`目标是为终端用户提供边输入边提示的即刻搜索功能,因此是一个轻量级搜索平台,不适用于数据量大时的搜索目的。总之,如果你需要在网页端或者APP为用户提供一个搜索条,然后支持输入容错、前缀搜索时,就可以使用它。 + * +### 代码Debug +* GDB + * [gdbgui](https://github.com/cs01/gdbgui) 提供浏览器支持的gdb debug工具,支持C,C++,Rust和Go. +* LLDB + * [CodeLLDB](https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb) — 专门为VSCode设计的LLDB Debug扩展 + +### 性能优化 +* [bheisler/criterion.rs](https://github.com/bheisler/criterion.rs) 比官方提供的benchmark库更好,目前已经成为事实上标准的性能测试工具 +* [Bytehound](https://github.com/koute/bytehound) Linux下的内存分析工具,可以用来分析:内存泄漏、内存分配、调用栈追踪,甚至它还有一个浏览器UI! 懂的人都懂,性能测试工具的UI服务是多么稀缺和珍贵! +* [llogiq/flame](https://github.com/llogiq/flame) 专为Rust打造的火焰图分析工具,可以告诉你程序在哪些代码上花费的时间过多,非常适合用于代码性能瓶颈的分析。与`perf`不同,`flame`库允许你自己定义想要测试的代码片段,只需要在代码前后加上相应的指令即可,非常好用 +* [sharkdp/hyperfine](https://github.com/sharkdp/hyperfine) 一个命令行benchmark工具,支持任意shell命令,支持缓存清除、预热、多次运行统计分析等,尽量保证结果的准确性 + + +### 多线程 +* 消息通道channel + * [**crossbeam-channel**](https://github.com/crossbeam-rs/crossbeam/tree/master/crossbeam-channel), 老牌强库,功能较全,性能较强,之前是独立的库,但是后面合并到了`crossbeam`主仓库中 + * [**flume**](https://github.com/zesterer/flume), 官方给出的性能数据要比crossbeam更好些,但是貌似最近没怎么更新 +* 并发原语(锁) + * [parking_lot](https://crates.io/crates/parking_lot), 社区较为活跃,star较多,更新较为活跃 + * [spin](https://crates.io/crates/spin), 在多数场景中性能比`parking_lot`高一点,最近没怎么更新 + * 如果不是追求特别极致的性能,建议选择前者 + +### 编解码 +* [Serde](https://github.com/serde-rs/serde) 一个超高性能的通用序列化/反序列化框架,可以跟多种协议的库联合使用,实现统一编解码格式 +* CSV + * [BurntSushi/rust-csv](https://github.com/BurntSushi/rust-csv) 高性能CSV读写库,支持[Serde](https://github.com/serde-rs/serde) +* JSON + * [serde-rs/json](https://github.com/serde-rs/json) 快到上天的JSON库,也是Rust事实上的标准JSON库,你也可以使用它的大哥[serde](https://github.com/serde-rs/serde),一个更通用的序列化/反序列化库 +* MsgPack + * [3Hren/msgpack-rust](https://github.com/3Hren/msgpack-rust) 纯Rust实现的MessagePack编解码协议 +* ProtocolBuffers + * [tokio-rs/prost](https://github.com/tokio-rs/prost) tokio出品,基本都属精品,此库也不例外,简单易用,文档详细 + * [stepancheg/rust-protobuf](https://github.com/stepancheg/rust-protobuf) 纯Rust实现 +* TOML + * [alexcrichton/toml-rs](https://github.com/alexcrichton/toml-rs) TOML编码/解码,可以配合`serde`使用 +* XML + * [tafia/quick-xml](https://github.com/tafia/quick-xml) 高性能XML库,可以配合`serde`使用,文档较为详细 +* YAML + * [dtolnay/serde-yaml](https://github.com/dtolnay/serde-yaml) 使用`serde`编解码`YAML`格式的数据 + +### UI 开发框架 +* 跨平台 + * [DioxusLabs/Dioxus](https://github.com/DioxusLabs/dioxus) 跨平台 UI 开发框架,支持 `WASM`、`Desktop`、`TUI` 等应用开发,文档较为详细 + +### Email +* [lettre/lettre](https://github.com/lettre/lettre) — Rust SMTP库 + +### 常用正则模版 diff --git a/src/awesome-empowering-js.md b/src/awesome-empowering-js.md new file mode 100644 index 0000000..93ee857 --- /dev/null +++ b/src/awesome-empowering-js.md @@ -0,0 +1,291 @@ +# 使用Rust增强Javascript +`Javascript`是目前全世界使用最广的语言(TIOBE排行榜比较迷,JS并没有排在第一位,我个人并不认同它的排名)。在过去这么多年中,围绕着`Javascript`已经建立了庞大的基础设施生态:例如使用`webpack`来将多个`js`文件打包成一个;使用`Babel`允许你用现代化的`js`语法编写兼容旧浏览器的代码;使用`Eslint`帮助开发找出代码中潜在的问题,类似`cargo clippy`。 + +以上的种种都在帮助`js`成为更好的语言和工具,它们是`Web`应用程序得以顺利、高效的开发和运行的基石。这些工具往往使用`Javascript`语言编写,一般来说,是没有问题的,但是在某些时候,可能会存在性能上的瓶颈或者安全隐患,因此阴差阳错、机缘巧合下,`Rust`成为了一个搅局者。 + +## Javascript基建库 +### deno +首先出场的自然是咖位最重的之一,可以说正是因为`deno`和`swc`的横空出世,才让一堆观望的大神对于Rust实现`Javascript`基建有了更强的信心。 + +`deno`是`node`半逆转后的字序,从此可以看出`deno`是`Node.js`的替代,它的目标是为`Typescript/Javascript`提供一个更现代化、更安全、更强大 的运行时,同时内置了很多强大的工具,可以用于打包、编译成可执行文件、文档、测试、lint等。 + +值得一提的是,`deno`的不少工具都使用了`swc`进行建造,包括代码审查、格式化、文档生成等。 + +通过包引入的方式来对比下`deno`和`node`,大家可以自己品味下。 + +```js +// node +const koa = require("koa" ); +const logger = require("@adesso/logger") + +// deno +import { Application } from "https://deno.land/x/oak/mod.ts"; +import { Logger } from "https://adesso.de/lib/logger.ts" +``` + + +### swc +[`swc`](https://github.com/swc-project/swc)是`Typescript/Javascript`编译器,它可以用来编译、压缩和打包JS,同时支持使用插件进行扩展,例如做代码变换等。 + +`swc`目前正在被一些知名项目所使用,包括`Next.js`,`Parcel`和`Deno`,还有些著名的公司也在使用它,例如`Vercel`、字节跳动、腾讯等。 + +它的性能非常非常高,官方号称,在单线程下比`Babel`快20倍,在4核心下比`Babel`快70倍! + +几个使用案例: + +- [nextjs 12](http://nextjs.org/12), 通过使用`swc`获得了更好的扩展性、性能以及wasm的支持,其中性能方面提升了3倍刷新速度、5倍打包速度 +- [Parcel](https://parceljs.org/),通过使用`swc`改善了10倍的性能 + +parcel screenshot + + + +官方还提供了一个在线运行的[demo](https://swc.rs/playground),功能齐全,可以试试。 + +swc screenshot + +### Rome +[`Rome`](https://github.com/rome/tools)可以用来对`JavaScript`、`TypeScript`、`HTML`、`JSON`、`Markdown` 和 `CSS` 进行lint、编译、打包等功能,它的目标是替代`Babel`、`ESLint`、`webpack`、`Prettier`、`Jest`等。 + +一开始`Rome`是使用`Typescript`开发,目前正在用`Rust`进行重写。有趣的是: `Rome`的作者也是`Babel`的作者, 后者还是他在学习编译原理时做的。 + + +### fnm +[`fnm`](https://github.com/Schniz/fnm)是一个简单易用、高性能的`Node`版本管理工具,还支持`.nvmrc`文件(`nvm`的`node`版本描述文件) + +fnm screenshot + +### boa +[`boa`](https://github.com/boa-dev/boa)是一个高性能的`javascript`词法分析器,解析器和解释器,目前还是实验性质的。 + +boa screenshot + +### napi +[`napi`](https://github.com/napi-rs/napi-rs)可以用于构建基于`Node API`的`Nodejs`插件,目前由`nextjs`主导开发。 + +### volt +[`volt`](https://github.com/voltpkg/volt)是一个现代化的、高性能、安全可靠的`Javascript`包管理工具。目前该库正处于活跃开发阶段,只供学习使用。 + +volt screenshot + +### neon +[`neon`](https://github.com/neon-bindings/neon)可以用于写安全、高性能的原生`Nodejs`模块。 + +```rust +fn make_an_array(mut cx: FunctionContext) -> JsResult { + // 创建一些值: + let n = cx.number(9000); + let s = cx.string("hello"); + let b = cx.boolean(true); + + // 创建一个新数组: + let array: Handle = cx.empty_array(); + + // 将值推入数组中 + array.set(&mut cx, 0, n)?; + array.set(&mut cx, 1, s)?; + array.set(&mut cx, 2, b)?; + + // 返回数组 + Ok(array) +} + +register_module!(mut cx, { + cx.export_function("makeAnArray", make_an_array) +}) +``` + +### resvg-js +[resvg-js](https://github.com/yisibl/resvg-js)是一个高性能`svg`渲染库,使用Rust + Typescript实现。下面的图片通过`svg`实现(羞~~~): + +resvg screenshot + +### deno_lint +[deno_lint](https://github.com/denoland/deno_lint), 由`deno`团队出品的`lint`工具,支持`Javascript/Typescript`,支持`Deno`也支持`Node`。 + +优点之一就是极致的快: +```shell +[ + { + "name": "deno_lint", + "totalMs": 105.3750100000002, + "runsCount": 5, + "measuredRunsAvgMs": 21.07500200000004, + "measuredRunsMs": [ + 24.79783199999997, + 19.563640000000078, + 20.759051999999883, + ] + }, + { + "name": "eslint", + "totalMs": 11845.073306000002, + "runsCount": 5, + "measuredRunsAvgMs": 2369.0146612000003, + "measuredRunsMs": [ + 2686.1039550000005, + 2281.501061, + 2298.6185210000003, + ] + } +] +``` + +### rslint +[rslint](https://github.com/rslint/rslint)是一个高性能、可定制性强、简单易用的`Javascript/Typescript` lint分析工具。 + +```shell +$ echo "let a = foo.hasOwnProperty('bar');" > foo.js +$ rslint ./foo.js +error[no-prototype-builtins]: do not access the object property `hasOwnProperty` directly from `foo` + ┌─ ./foo.js:1:9 + │ +1 │ let a = foo.hasOwnProperty('bar'); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^ + │ +help: get the function from the prototype of `Object` and call it + │ +1 │ let a = Object.prototype.hasOwnProperty.call(foo, 'bar'); + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + │ + ╧ note: the method may be shadowed and cause random bugs and denial of service vulnerabilities + +Outcome: 1 fail, 0 warn, 0 success + +help: for more information about the errors try the explain command: `rslint explain ` +``` + + +### rusty_v8 +[rusty_v8](https://github.com/denoland/rusty_v8)是`v8`的Rust语言绑定,底层封装了`c++ API`。 + + +## 用WASM增强JS + +### wasm +[wasm(web assembly)](https://webassembly.org/docs/use-cases/)是一种低级语言,它运行在浏览器中,可以和`javascript`相互调用,几乎所有浏览器都支持, 而且目前有多种高级语言都可以直接编译成`wasm`,更是大大增强了它的地位。 + +目前来说Rust可以编译成`wasm`,虽然还不够完美,但是它正在以肉眼可见的速度快速发展中。因此同时使用`Rust`和`Javascript`成为了一种可能:将`Rust`编译成`wasm`,再跟`js`进行交互,两者共生共存,各自解决擅长的场景(`wasm`性能高,`js`开发速度快)。 + +### yew +[`yew`](https://github.com/yewstack/yew)是一个正在活跃开发的`Rust/Wasm`框架,用于构建`Web`客户端应用。 + +yew screenshot + +### gloo +[gloo]是一个模块化的工具,使用`Rust/WASM`构建快速、可靠的`Web`应用。 + +```rust +use gloo::{events::EventListener, timers::callback::Timeout}; +use wasm_bindgen::prelude::*; + +pub struct DelayedHelloButton { + button: web_sys::Element, + on_click: events::EventListener, +} + +impl DelayedHelloButton { + pub fn new(document: &web_sys::Document) -> Result { + // 创建 ` + + + {{#if search_enabled}} + + {{/if}} + + +

{{ book_title }}

+ +
+ {{#if print_enable}} + + + + {{/if}} + {{#if git_repository_url}} + + + + {{/if}} + {{#if git_repository_edit_url}} + + + + {{/if}} + +
+ + + {{#if search_enabled}} + + {{/if}} + + + + +
+ +
+
+ {{{ content }}} +
+
+ + +
+ + + + + + + {{#if livereload}} + + + {{/if}} + + {{#if google_analytics}} + + + {{/if}} + + {{#if playground_line_numbers}} + + {{/if}} + + {{#if playground_copyable}} + + {{/if}} + + {{#if playground_js}} + + + + + + {{/if}} + + {{#if search_js}} + + + + {{/if}} + + + + + + + + + {{#each additional_js}} + + {{/each}} + + {{#if is_print}} + {{#if mathjax_support}} + + {{else}} + + {{/if}} + {{/if}} + + + \ No newline at end of file diff --git a/theme/style1.css b/theme/style1.css new file mode 100644 index 0000000..7dbc8b7 --- /dev/null +++ b/theme/style1.css @@ -0,0 +1,113 @@ +@media only screen and (max-width:1080px) { + .sidetoc { + display: none !important; + } +} + +@media only screen and (min-width:1080px) { + main { + position: relative; + padding-right: 170px; + } + .sidetoc { + margin-left: auto; + margin-right: auto; + /*left: calc(100% + (var(--content-max-width))/4 - 180px);*/ + left: calc(100% - 200px); + position: absolute; + } + .pagetoc { + position: fixed; + width: 200px; + height: calc(100vh - var(--menu-bar-height) - 10rem); + overflow: auto; + z-index: 1000; + } + .pagetoc a { + border-left: 1px solid var(--sidebar-bg); + color: var(--fg) !important; + display: block; + padding-bottom: 5px; + padding-top: 5px; + padding-left: 10px; + text-align: left; + text-decoration: none; + font-size: 1.2rem; + } + .pagetoc a:hover, + .pagetoc a.active { + background: var(--sidebar-bg); + color: var(--sidebar-fg) !important; + } + .pagetoc .active { + background: var(--sidebar-bg); + color: var(--sidebar-fg); + } +} + +.page-footer { + margin-top: 50px; + border-top: 1px solid #ccc; + overflow: hidden; + padding: 10px 0; + color: gray; +} + +/* 修改章节目录的间距 */ +.chapter li.chapter-item { + /* 没有文件时的文字颜色 */ + color: #939da3; + margin-top: 1rem; +} + +/* 修改滚动条宽度 */ +::-webkit-scrollbar { + width: 5px; + height: 5px; +} + +/* 表格靠左对齐 */ +table { + margin-left: 0 !important; +} + +/* 只使用底部的页面跳转,因为左右两边的宽跳转会被 page-toc 遮盖 */ +@media only screen and (max-width: 2560px) { + .nav-wide-wrapper { display: none; } + .nav-wrapper { display: block; } +} +@media only screen and (max-width: 2560px) { + .sidebar-visible .nav-wide-wrapper { display: none; } + .sidebar-visible .nav-wrapper { display: block; } +} + + +/* 修改顶部图标大小 */ +/* #menu-bar { + font-size: 17px; +} */ +/* 修改 github 样式 */ +.fa-github { + font-weight: 550; +} +.fa-github:after{ + content: "星光点点比不过你的 🌟"; + margin-left: 4px; +} + +/* Fix on mobile device */ +code { + word-break: break-word; +} + +/* 修复可编辑代码框顶部过窄的问题 */ +code.editable, .ace_scroller { + top: 10px; +} + +/* 修改书侧边目录的区域分隔行样式 */ +.chapter .spacer { + background-color: #99CCFF; + height: 2px; + margin-top: 8px; +} \ No newline at end of file