├── .gitignore ├── .travis.yml ├── CNAME ├── LICENSE-CC0 ├── Makefile ├── README.md ├── _cobalt.yml ├── _includes ├── _footer.liquid ├── _head.liquid └── _menu.liquid ├── _layouts ├── default.liquid ├── post.liquid └── site.liquid ├── assets ├── default.min.css └── highlight.min.js ├── bootstrap.css ├── demos ├── add │ ├── Makefile │ ├── add.rs │ ├── add.wasm │ ├── add.wat │ └── index.liquid ├── bundle.js ├── call-js │ ├── Makefile │ ├── call-js.rs │ ├── call-js.wasm │ ├── call-js.wat │ └── index.liquid ├── canvas │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Makefile │ ├── canvas.wasm │ ├── index.liquid │ ├── script.js │ └── src │ │ └── main.rs ├── factorial │ ├── Makefile │ ├── factorial.rs │ ├── factorial.wasm │ ├── factorial.wat │ └── index.liquid ├── feistel │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Makefile │ ├── feistel.rs │ ├── feistel.wasm │ ├── feistel.wat │ ├── index.liquid │ └── script.js ├── import-memory │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Makefile │ ├── index.liquid │ ├── string-passing.rs │ ├── string-passing.wasm │ └── string-passing.wat ├── index.md └── sha1 │ ├── .gitignore │ ├── Cargo.lock │ ├── Cargo.toml │ ├── Makefile │ ├── index.liquid │ ├── sha1-digest.rs │ ├── sha1-digest.wasm │ └── sha1-digest.wat ├── index.liquid ├── info.md ├── news.liquid ├── news ├── 2017-12-03-canvas.md ├── 2017-12-04-brainfuck-interpreter.md ├── 2017-12-04-rocket-a-game-on-wasm.md ├── 2017-12-05-semver-with-rust-and-wasm.md ├── 2017-12-06-the-case-for-wasm.md ├── 2017-12-07-import-memory.md ├── 2017-12-14-chip8-emulator-on-wasm.md ├── 2018-01-08-new-years-roundup.md ├── 2018-01-09-turtles-on-wasm.md ├── 2018-01-19-wasm-news.md ├── 2019-01-22-deprecation.md ├── big-factorial.md ├── minimal-example.md ├── native-wasm-target.md └── post-1.md ├── resources.md ├── robots.txt ├── setup ├── docker.md ├── emscripten.md ├── index.md └── wasm-target.md ├── style.css └── talks.md /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | *.big.wasm 3 | target 4 | drafts 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: rust 3 | 4 | before_script: 5 | - curl -LSfs https://japaric.github.io/trust/install.sh | sh -s -- --git cobalt-org/cobalt.rs --crate cobalt --force --target x86_64-unknown-linux-gnu 6 | - export PATH="$PATH:/home/travis/.cargo/bin" 7 | 8 | script: 9 | - cobalt build 10 | 11 | branches: 12 | only: 13 | - staging 14 | - trying 15 | - master 16 | 17 | deploy: 18 | provider: pages 19 | skip_cleanup: true 20 | github_token: $GH_TOKEN 21 | local_dir: build 22 | on: 23 | branch: master 24 | 25 | env: 26 | global: 27 | secure: CoyWcelKZrFd9rY+hTPaUU40VcrZ4BvC5yL9xdkci5B+v/mseNo73K16k9ONk9pDMmoqNGx0FqF/le+CBY1NSJtXAPRIJKuZix5VRLd4y3o+QYzySpEie/Afjvny3vaMTb8bb8WkMSwswt0QRTiqgj7Xc2SUFFlkGkv/hmLeMu2Rx8IBiWJy2mhLipaxrDd8tkuUPlOy+sgNNjChLZfh8PexYI6TJlt2uZef2wBp1iM2aQFgH5iH7Gf+CnG3cbEkr+1IdH312+0vCJQl1RdZ8noNVECa/2lYeTPdZlqCbw1jG9CJsaozVYaCx6sa5tdT/UwzqoGq6Go3BvzO3VGzSF454EHhOCSUK08jwE5yG5yxmhg/KfqM0zu6IrXFtrs/9B5toR8D2DtUkP7YvGpp47RByCLteESEpfddyZKMaZafnLEBH6nTXJAhbdKzXrvITB88kCnDL9eFyF+ikPJi0qtEriVg5h1XMNxnu5u0L7dRNcq7388nP0xklGuRYCsypUDCgT+9gOQfK/YfPFVn0SDiQngkCC5XFLKRuSyY5gQd9xAkQw6YRsYx2HSCjN1TWoPS50L/7muNhPeTESwhldzWckcQBvJnOrnPczbRmya5Lx5TG6QgwK7kNPILEdKMh5nmVTk4Db+FjMT2k6/6dWYiq/lNjTg8aPnIrV5DIr8= 28 | 29 | notifications: 30 | email: false 31 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | www.hellorust.com 2 | -------------------------------------------------------------------------------- /LICENSE-CC0: -------------------------------------------------------------------------------- 1 | CC0 1.0 Universal 2 | 3 | Statement of Purpose 4 | 5 | The laws of most jurisdictions throughout the world automatically confer 6 | exclusive Copyright and Related Rights (defined below) upon the creator and 7 | subsequent owner(s) (each and all, an "owner") of an original work of 8 | authorship and/or a database (each, a "Work"). 9 | 10 | Certain owners wish to permanently relinquish those rights to a Work for the 11 | purpose of contributing to a commons of creative, cultural and scientific 12 | works ("Commons") that the public can reliably and without fear of later 13 | claims of infringement build upon, modify, incorporate in other works, reuse 14 | and redistribute as freely as possible in any form whatsoever and for any 15 | purposes, including without limitation commercial purposes. These owners may 16 | contribute to the Commons to promote the ideal of a free culture and the 17 | further production of creative, cultural and scientific works, or to gain 18 | reputation or greater distribution for their Work in part through the use and 19 | efforts of others. 20 | 21 | For these and/or other purposes and motivations, and without any expectation 22 | of additional consideration or compensation, the person associating CC0 with a 23 | Work (the "Affirmer"), to the extent that he or she is an owner of Copyright 24 | and Related Rights in the Work, voluntarily elects to apply CC0 to the Work 25 | and publicly distribute the Work under its terms, with knowledge of his or her 26 | Copyright and Related Rights in the Work and the meaning and intended legal 27 | effect of CC0 on those rights. 28 | 29 | 1. Copyright and Related Rights. A Work made available under CC0 may be 30 | protected by copyright and related or neighboring rights ("Copyright and 31 | Related Rights"). Copyright and Related Rights include, but are not limited 32 | to, the following: 33 | 34 | i. the right to reproduce, adapt, distribute, perform, display, communicate, 35 | and translate a Work; 36 | 37 | ii. moral rights retained by the original author(s) and/or performer(s); 38 | 39 | iii. publicity and privacy rights pertaining to a person's image or likeness 40 | depicted in a Work; 41 | 42 | iv. rights protecting against unfair competition in regards to a Work, 43 | subject to the limitations in paragraph 4(a), below; 44 | 45 | v. rights protecting the extraction, dissemination, use and reuse of data in 46 | a Work; 47 | 48 | vi. database rights (such as those arising under Directive 96/9/EC of the 49 | European Parliament and of the Council of 11 March 1996 on the legal 50 | protection of databases, and under any national implementation thereof, 51 | including any amended or successor version of such directive); and 52 | 53 | vii. other similar, equivalent or corresponding rights throughout the world 54 | based on applicable law or treaty, and any national implementations thereof. 55 | 56 | 2. Waiver. To the greatest extent permitted by, but not in contravention of, 57 | applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and 58 | unconditionally waives, abandons, and surrenders all of Affirmer's Copyright 59 | and Related Rights and associated claims and causes of action, whether now 60 | known or unknown (including existing as well as future claims and causes of 61 | action), in the Work (i) in all territories worldwide, (ii) for the maximum 62 | duration provided by applicable law or treaty (including future time 63 | extensions), (iii) in any current or future medium and for any number of 64 | copies, and (iv) for any purpose whatsoever, including without limitation 65 | commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes 66 | the Waiver for the benefit of each member of the public at large and to the 67 | detriment of Affirmer's heirs and successors, fully intending that such Waiver 68 | shall not be subject to revocation, rescission, cancellation, termination, or 69 | any other legal or equitable action to disrupt the quiet enjoyment of the Work 70 | by the public as contemplated by Affirmer's express Statement of Purpose. 71 | 72 | 3. Public License Fallback. Should any part of the Waiver for any reason be 73 | judged legally invalid or ineffective under applicable law, then the Waiver 74 | shall be preserved to the maximum extent permitted taking into account 75 | Affirmer's express Statement of Purpose. In addition, to the extent the Waiver 76 | is so judged Affirmer hereby grants to each affected person a royalty-free, 77 | non transferable, non sublicensable, non exclusive, irrevocable and 78 | unconditional license to exercise Affirmer's Copyright and Related Rights in 79 | the Work (i) in all territories worldwide, (ii) for the maximum duration 80 | provided by applicable law or treaty (including future time extensions), (iii) 81 | in any current or future medium and for any number of copies, and (iv) for any 82 | purpose whatsoever, including without limitation commercial, advertising or 83 | promotional purposes (the "License"). The License shall be deemed effective as 84 | of the date CC0 was applied by Affirmer to the Work. Should any part of the 85 | License for any reason be judged legally invalid or ineffective under 86 | applicable law, such partial invalidity or ineffectiveness shall not 87 | invalidate the remainder of the License, and in such case Affirmer hereby 88 | affirms that he or she will not (i) exercise any of his or her remaining 89 | Copyright and Related Rights in the Work or (ii) assert any associated claims 90 | and causes of action with respect to the Work, in either case contrary to 91 | Affirmer's express Statement of Purpose. 92 | 93 | 4. Limitations and Disclaimers. 94 | 95 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 96 | surrendered, licensed or otherwise affected by this document. 97 | 98 | b. Affirmer offers the Work as-is and makes no representations or warranties 99 | of any kind concerning the Work, express, implied, statutory or otherwise, 100 | including without limitation warranties of title, merchantability, fitness 101 | for a particular purpose, non infringement, or the absence of latent or 102 | other defects, accuracy, or the present or absence of errors, whether or not 103 | discoverable, all to the greatest extent permissible under applicable law. 104 | 105 | c. Affirmer disclaims responsibility for clearing rights of other persons 106 | that may apply to the Work or any use thereof, including without limitation 107 | any person's Copyright and Related Rights in the Work. Further, Affirmer 108 | disclaims responsibility for obtaining any necessary consents, permissions 109 | or other rights required for any use of the Work. 110 | 111 | d. Affirmer understands and acknowledges that Creative Commons is not a 112 | party to this document and has no duty or obligation with respect to this 113 | CC0 or use of the Work. 114 | 115 | For more information, please see 116 | 117 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DEMOS=add call-js canvas factorial feistel import-memory 2 | 3 | build: 4 | cobalt build 5 | .PHONY: build 6 | 7 | serve: 8 | cobalt watch 9 | .PHONY: serve 10 | 11 | rebuild-demos: 12 | @for demo in $(DEMOS); do \ 13 | make -C demos/$$demo; \ 14 | done 15 | .PHONY: build-demos 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | This site is unmaintained and read-only. 4 | 5 | The [Rust and WebAssembly working group](https://rustwasm.github.io/) now provides a lot of tooling, documentation, tutorials and other material. 6 | For a complete introduction to Rust and WebAssembly check out the [Rust 🦀 and WebAssembly 🕸 book](https://rustwasm.github.io/book/). 7 | 8 | --- 9 | 10 | 11 | # [Hello, Rust!](https://www.hellorust.com/) 12 | 13 | ### A collection of resources, articles, examples and links regarding Rust & WebAssembly 14 | 15 | ## Website 16 | 17 | Everything is automatically deployed and available on [www.hellorust.com](https://www.hellorust.com/). 18 | 19 | ## Development 20 | 21 | The site is built using [Cobalt](https://github.com/cobalt-org/cobalt.rs). 22 | To compile everything and place it into the `build/` folder, execute: 23 | 24 | ``` 25 | cobalt build 26 | ``` 27 | 28 | Deployment is automatically run [through Travis CI](https://travis-ci.org/badboy/hellorust) on every push to the `master` branch. 29 | 30 | ### Developing examples 31 | 32 | Examples are placed in `demos/`. You can copy one of the existing examples or create a new one. 33 | Just pick a good short name as the directory name. 34 | 35 | Please add a `Makefile` which compiles and copies all necessary files in place on invocation. 36 | See [demos/add/Makefile](demos/add/Makefile) for a simple example. 37 | Once you have done that add the example's name to the main [Makefile](Makefile), so it can be rebuild easily. 38 | 39 | Add an `index.liquid` file for your example and make sure the following header is included: 40 | 41 | ``` 42 | permalink: /demos/slug-of-your-example 43 | title: "Demo: Name of your example" 44 | layout: site.liquid 45 | --- 46 | ``` 47 | 48 | Use HTML to structure your example explanation. 49 | 50 | ## Contributions 51 | 52 | All contributions are welcome. 53 | [Please open an issue](https://github.com/badboy/hellorust/issues/new) and tell us about your ideas. 54 | Pull Requests with new resources or examples are welcome as well. 55 | 56 | ## License [![CC0-badge]][CC0-deed] 57 | 58 | Texts and example code on [www.hellorust.com](https://www.hellorust.com/) are licensed under Creative Commons Zero v1.0 Universal License 59 | ([LICENSE-CC0](LICENSE-CC0) or https://creativecommons.org/publicdomain/zero/1.0/legalcode) 60 | 61 | Unless you explicitly state otherwise, any contribution intentionally submitted 62 | for inclusion in Hellorust by you, as defined in the CC0-1.0 license, shall be 63 | [dedicated to the public domain][CC0-deed] and licensed as above, without any additional 64 | terms or conditions. 65 | 66 | External dependencies used in example code remain under their own license. 67 | 68 | [CC0-deed]: https://creativecommons.org/publicdomain/zero/1.0/deed.en 69 | [CC0-badge]: https://mirrors.creativecommons.org/presskit/buttons/80x15/svg/cc-zero.svg 70 | -------------------------------------------------------------------------------- /_cobalt.yml: -------------------------------------------------------------------------------- 1 | source: "./" 2 | destination: build 3 | include_drafts: false 4 | default: 5 | excerpt_separator: "" 6 | is_draft: false 7 | pages: 8 | default: {} 9 | posts: 10 | title: ~ 11 | description: ~ 12 | dir: news 13 | drafts_dir: drafts 14 | order: Desc 15 | rss: feed.xml 16 | jsonfeed: ~ 17 | default: {} 18 | site: 19 | title: "Hello, Rust!" 20 | description: "Rust for the Web" 21 | base_url: "https://www.hellorust.com" 22 | data: ~ 23 | template_extensions: 24 | - md 25 | - liquid 26 | ignore: 27 | - README.md 28 | - Makefile 29 | assets: 30 | sass: 31 | style: Nested 32 | -------------------------------------------------------------------------------- /_includes/_footer.liquid: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /_includes/_head.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {{page.title}} - Hello, Rust! 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /_includes/_menu.liquid: -------------------------------------------------------------------------------- 1 |
2 | 23 |
24 | -------------------------------------------------------------------------------- /_layouts/default.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include "_head.liquid" %} 5 | 6 | 7 | 8 | {% include "_menu.liquid" %} 9 | 10 |
11 |
12 |

13 | The Rust Community team is dedicated to bringing new people into the 14 | Rust Programming Language's community, 15 | and helping established Rust communities thrive. 16 |

17 |
18 |
19 |
20 | 21 |
22 |

The Community Team

23 |
    24 |
  • Was announced in June 2015
  • 25 |
  • Exists to help: 26 |
      27 |
    • Create and curate reusable community materials
    • 28 |
    • Learn from and assist other tech communities
    • 29 |
    • Curate and publicize the ecosystem of Rust projects
    • 30 |
    • Lead outreach efforts
    • 31 |
    • Help coordinate user-led meetups and conferences
    • 32 |
    33 |
  • 34 |
35 |
36 | 37 |
38 |

Find a Meetup or conference

39 | 44 |
45 | 46 |
47 |

Get Involved

48 | 52 |
53 | 54 |
55 |

Promote Rust

56 | 59 |
60 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /_layouts/post.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include "_head.liquid" %} 5 | 6 | 7 | 8 | {% include "_menu.liquid" %} 9 | 10 |
11 |
12 |
13 |

{{ page.title }}

14 | 15 | - {{ page.data.author }} 16 | 17 | 18 | {{ page.content }} 19 |
20 |
21 |
22 | 23 | {% include "_footer.liquid" %} 24 | 25 | 26 | -------------------------------------------------------------------------------- /_layouts/site.liquid: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include "_head.liquid" %} 5 | 6 | 7 | 8 | {% include "_menu.liquid" %} 9 | 10 |
11 |
12 | {{ page.content }} 13 |
14 |
15 | 16 | {% include "_footer.liquid" %} 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /assets/default.min.css: -------------------------------------------------------------------------------- 1 | .hljs{display:block;overflow-x:auto;padding:0.5em;background:#F0F0F0}.hljs,.hljs-subst{color:#444}.hljs-comment{color:#888888}.hljs-keyword,.hljs-attribute,.hljs-selector-tag,.hljs-meta-keyword,.hljs-doctag,.hljs-name{font-weight:bold}.hljs-type,.hljs-string,.hljs-number,.hljs-selector-id,.hljs-selector-class,.hljs-quote,.hljs-template-tag,.hljs-deletion{color:#880000}.hljs-title,.hljs-section{color:#880000;font-weight:bold}.hljs-regexp,.hljs-symbol,.hljs-variable,.hljs-template-variable,.hljs-link,.hljs-selector-attr,.hljs-selector-pseudo{color:#BC6060}.hljs-literal{color:#78A960}.hljs-built_in,.hljs-bullet,.hljs-code,.hljs-addition{color:#397300}.hljs-meta{color:#1f7199}.hljs-meta-string{color:#4d99bf}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:bold} -------------------------------------------------------------------------------- /assets/highlight.min.js: -------------------------------------------------------------------------------- 1 | /*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ 2 | !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/&/g,"&").replace(//g,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return w(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||w(i))return i}function o(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){s+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var l=0,s="",f=[];e.length||r.length;){var g=i();if(s+=n(a.substring(l,g[0].offset)),l=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===l);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return s+n(a.substr(l))}function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map(function(n){return o(e,{v:null},n)})),e.cached_variants||e.eW&&[o(e)]||[e]}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var o={},u=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");o[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?u("keyword",a.k):x(a.k).forEach(function(e){u(e,a.k[e])}),a.k=o}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]),a.c=Array.prototype.concat.apply([],a.c.map(function(e){return l("self"===e?a:e)})),a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var c=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=c.length?t(c.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function l(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":I.classPrefix,i='',i+n+o}function h(){var e,t,r,a;if(!E.k)return n(k);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(k);r;)a+=n(k.substring(t,r.index)),e=l(E,r),e?(B+=e[1],a+=p(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(k);return a+n(k.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!y[E.sL])return n(k);var t=e?f(E.sL,k,!0,x[E.sL]):g(k,E.sL.length?E.sL:void 0);return E.r>0&&(B+=t.r),e&&(x[E.sL]=t.top),p(t.language,t.value,!1,!0)}function b(){L+=null!=E.sL?d():h(),k=""}function v(e){L+=e.cN?p(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(k+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?k+=n:(t.eB&&(k+=n),b(),t.rB||t.eB||(k=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?k+=n:(a.rE||a.eE||(k+=n),b(),a.eE&&(k=n));do E.cN&&(L+=C),E.skip||(B+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return k+=n,n.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,E=i||N,x={},L="";for(R=E;R!==N;R=R.parent)R.cN&&(L=p(R.cN,"",!0)+L);var k="",B=0;try{for(var M,j,O=0;;){if(E.t.lastIndex=O,M=E.t.exec(t),!M)break;j=m(t.substring(O,M.index),M[0]),O=M.index+j}for(m(t.substr(O)),R=E;R.parent;R=R.parent)R.cN&&(L+=C);return{r:B,value:L,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function g(e,t){t=t||I.languages||x(y);var r={r:0,value:n(e)},a=r;return t.filter(w).forEach(function(n){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function p(e){return I.tabReplace||I.useBR?e.replace(M,function(e,n){return I.useBR&&"\n"===e?"
":I.tabReplace?n.replace(/\t/g,I.tabReplace):""}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function d(e){var n,t,r,o,l,s=i(e);a(s)||(I.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,l=n.textContent,r=s?f(s,l,!0):g(l),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),l)),r.value=p(r.value),e.innerHTML=r.value,e.className=h(e.className,s,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function b(e){I=o(I,e)}function v(){if(!v.called){v.called=!0;var e=document.querySelectorAll("pre code");E.forEach.call(e,d)}}function m(){addEventListener("DOMContentLoaded",v,!1),addEventListener("load",v,!1)}function N(n,t){var r=y[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function R(){return x(y)}function w(e){return e=(e||"").toLowerCase(),y[e]||y[L[e]]}var E=[],x=Object.keys,y={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",I={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};return e.highlight=f,e.highlightAuto=g,e.fixMarkup=p,e.highlightBlock=d,e.configure=b,e.initHighlighting=v,e.initHighlightingOnLoad=m,e.registerLanguage=N,e.listLanguages=R,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("xml",function(s){var e="[A-Za-z0-9\\._:-]+",t={eW:!0,i:/`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],cI:!0,c:[{cN:"meta",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0}]},{cN:"tag",b:"|$)",e:">",k:{name:"style"},c:[t],starts:{e:"",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[t],starts:{e:"",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"meta",v:[{b:/<\?xml/,e:/\?>/,r:10},{b:/<\?\w+/,e:/\?>/}]},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},t]}]}});hljs.registerLanguage("rust",function(e){var t="([ui](8|16|32|64|128|size)|f(32|64))?",r="alignof as be box break const continue crate do else enum extern false fn for if impl in let loop match mod mut offsetof once priv proc pub pure ref return self Self sizeof static struct super trait true type typeof unsafe unsized use virtual while where yield move default",n="drop i8 i16 i32 i64 i128 isize u8 u16 u32 u64 u128 usize f32 f64 str char bool Box Option Result String Vec Copy Send Sized Sync Drop Fn FnMut FnOnce ToOwned Clone Debug PartialEq PartialOrd Eq Ord AsRef AsMut Into From Default Iterator Extend IntoIterator DoubleEndedIterator ExactSizeIterator SliceConcatExt ToString assert! assert_eq! bitflags! bytes! cfg! col! concat! concat_idents! debug_assert! debug_assert_eq! env! panic! file! format! format_args! include_bin! include_str! line! local_data_key! module_path! option_env! print! println! select! stringify! try! unimplemented! unreachable! vec! write! writeln! macro_rules! assert_ne! debug_assert_ne!";return{aliases:["rs"],k:{keyword:r,literal:"true false Some None Ok Err",built_in:n},l:e.IR+"!?",i:""}]}});hljs.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"section",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"quote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"^```w*s*$",e:"^```s*$"},{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"string",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"symbol",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:/^\[[^\n]+\]:/,rB:!0,c:[{cN:"symbol",b:/\[/,e:/\]/,eB:!0,eE:!0},{cN:"link",b:/:\s*/,e:/$/,eB:!0}]}]}});hljs.registerLanguage("lisp",function(b){var e="[a-zA-Z_\\-\\+\\*\\/\\<\\=\\>\\&\\#][a-zA-Z0-9_\\-\\+\\*\\/\\<\\=\\>\\&\\#!]*",c="\\|[^]*?\\|",r="(\\-|\\+)?\\d+(\\.\\d+|\\/\\d+)?((d|e|f|l|s|D|E|F|L|S)(\\+|\\-)?\\d+)?",a={cN:"meta",b:"^#!",e:"$"},l={cN:"literal",b:"\\b(t{1}|nil)\\b"},n={cN:"number",v:[{b:r,r:0},{b:"#(b|B)[0-1]+(/[0-1]+)?"},{b:"#(o|O)[0-7]+(/[0-7]+)?"},{b:"#(x|X)[0-9a-fA-F]+(/[0-9a-fA-F]+)?"},{b:"#(c|C)\\("+r+" +"+r,e:"\\)"}]},i=b.inherit(b.QSM,{i:null}),t=b.C(";","$",{r:0}),s={b:"\\*",e:"\\*"},u={cN:"symbol",b:"[:&]"+e},d={b:e,r:0},f={b:c},m={b:"\\(",e:"\\)",c:["self",l,i,n,d]},o={c:[n,i,s,u,m,d],v:[{b:"['`]\\(",e:"\\)"},{b:"\\(quote ",e:"\\)",k:{name:"quote"}},{b:"'"+c}]},v={v:[{b:"'"+e},{b:"#'"+e+"(::"+e+")*"}]},N={b:"\\(\\s*",e:"\\)"},A={eW:!0,r:0};return N.c=[{cN:"name",v:[{b:e},{b:c}]},A],A.c=[o,v,N,l,n,i,t,s,u,f,d],{i:/\S/,c:[n,a,l,i,t,o,v,N,d]}});hljs.registerLanguage("cpp",function(t){var e={cN:"keyword",b:"\\b[a-z\\d_]*_t\\b"},r={cN:"string",v:[{b:'(u8?|U)?L?"',e:'"',i:"\\n",c:[t.BE]},{b:'(u8?|U)?R"',e:'"',c:[t.BE]},{b:"'\\\\?.",e:"'",i:"."}]},s={cN:"number",v:[{b:"\\b(0b[01']+)"},{b:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{b:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],r:0},i={cN:"meta",b:/#\s*[a-z]+\b/,e:/$/,k:{"meta-keyword":"if else elif endif define undef warning error line pragma ifdef ifndef include"},c:[{b:/\\\n/,r:0},t.inherit(r,{cN:"meta-string"}),{cN:"meta-string",b:/<[^\n>]*>/,e:/$/,i:"\\n"},t.CLCM,t.CBCM]},a=t.IR+"\\s*\\(",c={keyword:"int float while private char catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignof constexpr decltype noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and or not",built_in:"std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr",literal:"true false nullptr NULL"},n=[e,t.CLCM,t.CBCM,s,r];return{aliases:["c","cc","h","c++","h++","hpp"],k:c,i:"",k:c,c:["self",e]},{b:t.IR+"::",k:c},{v:[{b:/=/,e:/;/},{b:/\(/,e:/\)/},{bK:"new throw return else",e:/;/}],k:c,c:n.concat([{b:/\(/,e:/\)/,k:c,c:n.concat(["self"]),r:0}]),r:0},{cN:"function",b:"("+t.IR+"[\\*&\\s]+)+"+a,rB:!0,e:/[{;=]/,eE:!0,k:c,i:/[^\w\s\*&]/,c:[{b:a,rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:c,r:0,c:[t.CLCM,t.CBCM,r,s,e]},t.CLCM,t.CBCM,i]},{cN:"class",bK:"class struct",e:/[{;:]/,c:[{b://,c:["self"]},t.TM]}]),exports:{preprocessor:i,strings:r,k:c}}});hljs.registerLanguage("json",function(e){var i={literal:"true false null"},n=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:n,k:i},t={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(r,{b:/:/})],i:"\\S"},c={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return n.splice(n.length,0,t,c),{c:n,k:i,i:"\\S"}});hljs.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},s={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/\b-?[a-z\._]+\b/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"meta",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,s,a,t]}});hljs.registerLanguage("shell",function(s){return{aliases:["console"],c:[{cN:"meta",b:"^\\s{0,3}[\\w\\d\\[\\]()@-]*[>%$#]",starts:{e:"$",sL:"bash"}}]}});hljs.registerLanguage("diff",function(e){return{aliases:["patch"],c:[{cN:"meta",r:10,v:[{b:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"comment",v:[{b:/Index: /,e:/$/},{b:/={3,}/,e:/$/},{b:/^\-{3}/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+{3}/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"addition",b:"^\\!",e:"$"}]}});hljs.registerLanguage("javascript",function(e){var r="[A-Za-z$_][0-9A-Za-z$_]*",t={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},n={cN:"subst",b:"\\$\\{",e:"\\}",k:t,c:[]},c={cN:"string",b:"`",e:"`",c:[e.BE,n]};n.c=[e.ASM,e.QSM,c,a,e.RM];var s=n.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:t,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,c,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:r+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:r,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+r+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:r},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,c:s}]}]},{b://,sL:"xml",c:[{b:/<\w+\s*\/>/,skip:!0},{b:/<\w+/,e:/(\/\w+|\w+\/)>/,skip:!0,c:[{b:/<\w+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:r}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:s}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}});hljs.registerLanguage("makefile",function(e){var i={cN:"variable",v:[{b:"\\$\\("+e.UIR+"\\)",c:[e.BE]},{b:/\$[@% $@ 7 | 8 | %.wasm: %.big.wasm 9 | wasm-gc $< $@ 10 | 11 | %.big.wasm: %.rs 12 | rustc +nightly --target wasm32-unknown-unknown -O --crate-type=cdylib $< -o $@ 13 | -------------------------------------------------------------------------------- /demos/add/add.rs: -------------------------------------------------------------------------------- 1 | #[no_mangle] 2 | pub extern "C" fn add_one(x: i32) -> i32 { 3 | x + 1 4 | } 5 | -------------------------------------------------------------------------------- /demos/add/add.wasm: -------------------------------------------------------------------------------- 1 | asm ``p*memoryadd_onerust_eh_personality  2 |  Aj  3 | A  -------------------------------------------------------------------------------- /demos/add/add.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i32) (result i32))) 3 | (type (;1;) (func)) 4 | (func (;0;) (type 0) (param i32) (result i32) 5 | get_local 0 6 | i32.const 1 7 | i32.add) 8 | (func (;1;) (type 1)) 9 | (table (;0;) 0 anyfunc) 10 | (memory (;0;) 17) 11 | (export "memory" (memory 0)) 12 | (export "add_one" (func 0)) 13 | (export "rust_eh_personality" (func 1)) 14 | (data (i32.const 4) "\10\00\10\00")) 15 | -------------------------------------------------------------------------------- /demos/add/index.liquid: -------------------------------------------------------------------------------- 1 | permalink: /demos/add 2 | title: "Demo: Minimal add function with no_std" 3 | layout: site.liquid 4 | --- 5 |

Minimal Rust & WebAssembly example

6 | 7 |

8 | Click the button to run the WebAssembly code. 9 |

10 | 11 |

12 | Based on 13 | an example 14 | by Steve Klabnik. 15 |

16 | 17 | 18 | 19 | 30 | 31 |
32 | 33 |

The Code

34 | 35 |

36 | Download add.rs 37 |
38 | Download add.wat (WebAssembly text format) 39 |

40 | 41 |
#[no_mangle]
42 | pub extern "C" fn add_one(x: i32) -> i32 {
43 |     x + 1
44 | }
45 | 
46 | 47 |

48 | Compile this code as follows: 49 |

50 | 51 |
rustup update
52 | rustup target add wasm32-unknown-unknown --toolchain nightly
53 | rustc +nightly --target wasm32-unknown-unknown -O --crate-type=cdylib add.rs -o add.big.wasm
54 | 55 |

Reduce the size by removing unused stuff from the module:

56 | 57 |
cargo install --git https://github.com/alexcrichton/wasm-gc 
58 | wasm-gc add.big.wasm add.wasm
59 | 60 |

61 | The JavaScript code loads the WebAssembly module and has access to the exported function. 62 |

63 | 64 |
fetch('add.wasm')
65 | .then(response => response.arrayBuffer())
66 | .then(bytes => WebAssembly.instantiate(bytes, {}))
67 | .then(results => {
68 |     alert(results.instance.exports.add_one(41));
69 | });
70 | 71 |

72 | The readable Webassembly looks like this: 73 |

74 | 75 |
(module
76 |   (type (;0;) (func (param i32) (result i32)))
77 |   (type (;1;) (func))
78 |   (func (;0;) (type 0) (param i32) (result i32)
79 |     get_local 0
80 |     i32.const 1
81 |     i32.add)
82 |   (func (;1;) (type 1))
83 |   (table (;0;) 0 anyfunc)
84 |   (memory (;0;) 17)
85 |   (export "memory" (memory 0))
86 |   (export "add_one" (func 0))
87 |   (export "rust_eh_personality" (func 1))
88 |   (data (i32.const 4) "\10\00\10\00"))
89 | -------------------------------------------------------------------------------- /demos/bundle.js: -------------------------------------------------------------------------------- 1 | function fetchAndInstantiate(url, importObject) { 2 | return fetch(url).then(response => 3 | response.arrayBuffer() 4 | ).then(bytes => 5 | WebAssembly.instantiate(bytes, importObject) 6 | ).then(results => 7 | results.instance 8 | ); 9 | } 10 | 11 | // Copy a nul-terminated string from the buffer pointed to. 12 | // Consumes the old data and thus deallocated it. 13 | function copyCStr(module, ptr) { 14 | let orig_ptr = ptr; 15 | const collectCString = function* () { 16 | let memory = new Uint8Array(module.memory.buffer); 17 | while (memory[ptr] !== 0) { 18 | if (memory[ptr] === undefined) { throw new Error("Tried to read undef mem") } 19 | yield memory[ptr] 20 | ptr += 1 21 | } 22 | } 23 | 24 | const buffer_as_u8 = new Uint8Array(collectCString()) 25 | const utf8Decoder = new TextDecoder("UTF-8"); 26 | const buffer_as_utf8 = utf8Decoder.decode(buffer_as_u8); 27 | module.dealloc_str(orig_ptr); 28 | return buffer_as_utf8 29 | } 30 | 31 | function getStr(module, ptr, len) { 32 | const getData = function* (ptr, len) { 33 | let memory = new Uint8Array(module.memory.buffer); 34 | for (let index = 0; index < len; index++) { 35 | if (memory[ptr] === undefined) { throw new Error(`Tried to read undef mem at ${ptr}`) } 36 | yield memory[ptr + index] 37 | } 38 | } 39 | 40 | const buffer_as_u8 = new Uint8Array(getData(ptr/8, len/8)); 41 | const utf8Decoder = new TextDecoder("UTF-8"); 42 | const buffer_as_utf8 = utf8Decoder.decode(buffer_as_u8); 43 | return buffer_as_utf8; 44 | } 45 | 46 | function newString(module, str) { 47 | const utf8Encoder = new TextEncoder("UTF-8"); 48 | let string_buffer = utf8Encoder.encode(str) 49 | let len = string_buffer.length 50 | let ptr = module.alloc(len+1) 51 | 52 | let memory = new Uint8Array(module.memory.buffer); 53 | for (i = 0; i < len; i++) { 54 | memory[ptr+i] = string_buffer[i] 55 | } 56 | 57 | memory[ptr+len] = 0; 58 | 59 | return ptr; 60 | } 61 | -------------------------------------------------------------------------------- /demos/call-js/Makefile: -------------------------------------------------------------------------------- 1 | NAME=call-js 2 | 3 | build: $(NAME).wasm $(NAME).wat 4 | 5 | %.wat: %.wasm 6 | wasm2wat $< > $@ 7 | 8 | %.wasm: %.big.wasm 9 | wasm-gc $< $@ 10 | 11 | %.big.wasm: %.rs 12 | rustc +nightly --target wasm32-unknown-unknown -O --crate-type=cdylib $< -o $@ 13 | -------------------------------------------------------------------------------- /demos/call-js/call-js.rs: -------------------------------------------------------------------------------- 1 | extern { 2 | fn alert(ptr: *const u8, number: u32); 3 | } 4 | 5 | #[no_mangle] 6 | pub extern "C" fn run() { 7 | unsafe { 8 | let x = b"Hello World!\0"; 9 | alert(x as *const u8, 42); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /demos/call-js/call-js.wasm: -------------------------------------------------------------------------------- 1 | asm `` envalertp&memoryrunrust_eh_personality  2 | A A*  A  A Hello World! -------------------------------------------------------------------------------- /demos/call-js/call-js.wat: -------------------------------------------------------------------------------- 1 | (module 2 | (type (;0;) (func (param i32 i32))) 3 | (type (;1;) (func)) 4 | (import "env" "alert" (func (;0;) (type 0))) 5 | (func (;1;) (type 1) 6 | i32.const 12 7 | i32.const 42 8 | call 0) 9 | (func (;2;) (type 1)) 10 | (table (;0;) 0 anyfunc) 11 | (memory (;0;) 17) 12 | (export "memory" (memory 0)) 13 | (export "run" (func 1)) 14 | (export "rust_eh_personality" (func 2)) 15 | (data (i32.const 4) " \00\10\00") 16 | (data (i32.const 12) "Hello World!\00")) 17 | -------------------------------------------------------------------------------- /demos/call-js/index.liquid: -------------------------------------------------------------------------------- 1 | permalink: "/demos/call-js" 2 | title: "Demo: Call JavaScript from Rust" 3 | layout: site.liquid 4 | --- 5 |

Demo: Call JavaScript from Rust

6 | 7 |

8 | Click the button to run the WebAssembly code. 9 |

10 | 11 | 12 | 13 | 14 | 36 | 37 |
38 | 39 |

The Code

40 | 41 |

42 | Download call-js.rs 43 |
44 | Download call-js.wat (WebAssembly text format) 45 |

46 | 47 |
extern {
48 |     fn alert(ptr: *const u8, number: u32);
49 | }
50 | 
51 | #[no_mangle]
52 | pub extern "C" fn run() {
53 |     unsafe {
54 |         let x = b"Hello World!\0";
55 |         alert(x as *const u8, 42);
56 |     }
57 | }
58 | 59 |

60 | The JavaScript code loads the WebAssembly module and has access to the exported function. 61 |

62 | 63 |
window.Module = {};
64 | 
65 | const imports = {
66 |   env: {
67 |     alert: function(ptr, number) {
68 |       let str = copyCStr(Module, ptr);
69 |       alert(str + " -> " + number);
70 |     }
71 |   }
72 | };
73 | 
74 | fetchAndInstantiate("./call-js.wasm", imports)
75 | .then(mod => {
76 |   Module.memory      = mod.exports.memory;
77 |   Module.run         = mod.exports.run;
78 |   Module.dealloc_str = function() {}
79 | 
80 |   Module.run();
81 | });
82 | 
83 | -------------------------------------------------------------------------------- /demos/canvas/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "rust-wasm-canvas" 3 | version = "0.1.0" 4 | 5 | -------------------------------------------------------------------------------- /demos/canvas/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rust-wasm-canvas" 3 | version = "0.1.0" 4 | 5 | [dependencies] 6 | 7 | [profile.release] 8 | opt-level = "s" 9 | -------------------------------------------------------------------------------- /demos/canvas/Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | cargo +nightly build --target=wasm32-unknown-unknown --release 3 | wasm-gc ./target/wasm32-unknown-unknown/release/rust-wasm-canvas.wasm ./canvas.wasm 4 | -------------------------------------------------------------------------------- /demos/canvas/canvas.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badboy/hellorust/f2dce7d25cd0a07b12a82c6738f205b1e67d8ee7/demos/canvas/canvas.wasm -------------------------------------------------------------------------------- /demos/canvas/index.liquid: -------------------------------------------------------------------------------- 1 | permalink: /demos/canvas 2 | title: "Demo: Update a canvas from wasm" 3 | layout: site.liquid 4 | --- 5 |

Update a canvas from wasm

6 | 7 |

8 | by Geoffroy Couprie 9 |

10 | 11 | 12 |
13 |

14 | Caution: This will display a flickering image. 15 |

16 | 17 | 18 | 19 | 20 |
21 | 22 |

The Code

23 | 24 |

25 | This example shares a buffer between the Javascript side and the Rust side, and uses 26 | it to update the raw pixels on a canvas. 27 |

28 |

29 | Download main.rs 30 |

31 | 32 |
use std::mem;
 33 | use std::slice;
 34 | use std::os::raw::c_void;
 35 | 
 36 | // We need to provide an (empty) main function,
 37 | // as the target currently is compiled as a binary.
 38 | fn main() {}
 39 | 
 40 | // In order to work with the memory we expose (de)allocation methods
 41 | #[no_mangle]
 42 | pub extern "C" fn alloc(size: usize) -> *mut c_void {
 43 |     let mut buf = Vec::with_capacity(size);
 44 |     let ptr = buf.as_mut_ptr();
 45 |     mem::forget(buf);
 46 |     return ptr as *mut c_void;
 47 | }
 48 | 
 49 | #[no_mangle]
 50 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
 51 |     unsafe  {
 52 |         let _buf = Vec::from_raw_parts(ptr, 0, cap);
 53 |     }
 54 | }
 55 | 
 56 | // the Javascript side passes a pointer to a buffer, the size of the corresponding canvas
 57 | // and the current timestamp
 58 | #[no_mangle]
 59 | pub extern "C" fn fill(pointer: *mut u8, max_width: usize, max_height: usize, time: f64) {
 60 | 
 61 |     // pixels are stored in RGBA, so each pixel is 4 bytes
 62 |     let byte_size = max_width * max_height * 4;
 63 |     let sl = unsafe { slice::from_raw_parts_mut(pointer, byte_size) };
 64 | 
 65 |     for i in 0..byte_size {
 66 | 
 67 |         // get the position of current pixel
 68 |         let height = i / 4 / max_width;
 69 |         let width  = i / 4 % max_width;
 70 | 
 71 |         if i%4 == 3 {
 72 |             // set opacity to 1
 73 |             sl[i] = 255;
 74 |         } else if i%4 == 0 {
 75 |             // create a red ripple effect from the top left corner
 76 |             let len = ((height*height + width*width) as f64).sqrt();
 77 |             let nb = time  + len / 4.0;
 78 |             let a = 128.0 + nb.cos() * 128.0;
 79 |             sl[i] = a as u8;
 80 |         } else if i % 4 == 2 {
 81 |             // create a blue ripple effect from the top right corner
 82 |             let width = 500 - width;
 83 |             let len = ((height*height + width*width) as f64).sqrt();
 84 |             let nb = time  + len / 4.0;
 85 |             let a = 128.0 + nb.cos() * 128.0;
 86 |             sl[i] = a as u8;
 87 |         }
 88 |     }
 89 | }
 90 | 
91 | 92 | 93 |

94 | The JavaScript code loads the WebAssembly module and has access to the exported functions and fields, including the allocation and memory. 95 | We create an ImageData backed by memory allocated on the Rust side, and regularly update it. 96 |

97 | 98 |
fetch("canvas.wasm").then(response =>
 99 |   response.arrayBuffer()
100 | ).then(bytes =>
101 | 
102 |   // the Rust side needs a cos function
103 |   WebAssembly.instantiate(bytes, { env: { cos: Math.cos } })
104 | ).then(results => {
105 |   let module = {};
106 |   let mod = results.instance;
107 |   module.alloc   = mod.exports.alloc;
108 |   module.dealloc = mod.exports.dealloc;
109 |   module.fill    = mod.exports.fill;
110 | 
111 |   var width  = 500;
112 |   var height = 500;
113 | 
114 | 
115 |   var canvas = document.getElementById('screen');
116 |   if (canvas.getContext) {
117 |     var ctx = canvas.getContext('2d');
118 | 
119 |     let byteSize = width * height * 4;
120 |     var pointer = module.alloc( byteSize );
121 | 
122 |     var usub = new Uint8ClampedArray(mod.exports.memory.buffer, pointer, byteSize);
123 |     var img = new ImageData(usub, width, height);
124 | 
125 |     var start = null;
126 |     function step(timestamp) {
127 |       var progress;
128 |       if (start === null) start = timestamp;
129 |       progress = timestamp - start;
130 |       if (progress > 100) {
131 |         module.fill(pointer, width, height, timestamp);
132 | 
133 |         start = timestamp
134 | 
135 |         window.requestAnimationFrame(draw);
136 |       } else {
137 |         window.requestAnimationFrame(step);
138 |       }
139 |     }
140 | 
141 |     function draw() {
142 |       ctx.putImageData(img, 0, 0)
143 |       window.requestAnimationFrame(step);
144 |     }
145 | 
146 |     window.requestAnimationFrame(step);
147 |   }
148 | 
149 | });
150 | 
151 | -------------------------------------------------------------------------------- /demos/canvas/script.js: -------------------------------------------------------------------------------- 1 | fetch("canvas.wasm").then(response => 2 | response.arrayBuffer() 3 | ).then(bytes => 4 | WebAssembly.instantiate(bytes, { env: { cos: Math.cos } }) 5 | ).then(results => { 6 | let module = {}; 7 | let mod = results.instance; 8 | module.alloc = mod.exports.alloc; 9 | module.dealloc = mod.exports.dealloc; 10 | module.fill = mod.exports.fill; 11 | 12 | var width = 500; 13 | var height = 500; 14 | 15 | let byteSize = width * height * 4; 16 | var pointer = module.alloc( byteSize ); 17 | var buffer = new Uint8Array(mod.exports.memory.buffer, pointer, byteSize); 18 | 19 | var canvas = document.getElementById('screen'); 20 | if (canvas.getContext) { 21 | var ctx = canvas.getContext('2d'); 22 | 23 | var pointer = module.alloc( byteSize ); 24 | 25 | var usub = new Uint8ClampedArray(mod.exports.memory.buffer, pointer, byteSize); 26 | var img = new ImageData(usub, width, height); 27 | var running = false; 28 | 29 | var start = null; 30 | function step(timestamp) { 31 | var progress; 32 | if (start === null) start = timestamp; 33 | progress = timestamp - start; 34 | if (progress > 100) { 35 | module.fill(pointer, width, height, timestamp); 36 | 37 | start = timestamp 38 | 39 | if (running) 40 | window.requestAnimationFrame(draw); 41 | } else { 42 | if (running) 43 | window.requestAnimationFrame(step); 44 | } 45 | } 46 | 47 | function draw() { 48 | ctx.putImageData(img, 0, 0) 49 | window.requestAnimationFrame(step); 50 | } 51 | 52 | //if (running) 53 | //window.requestAnimationFrame(step); 54 | var button = document.getElementById("run-wasm"); 55 | button.addEventListener("click", function(e) { 56 | running = !running; 57 | if (running) { 58 | button.innerText = "Stop motion"; 59 | window.requestAnimationFrame(step); 60 | } else { 61 | button.innerText = "Start motion"; 62 | } 63 | }); 64 | } 65 | }); 66 | 67 | -------------------------------------------------------------------------------- /demos/canvas/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::slice; 3 | use std::os::raw::c_void; 4 | 5 | fn main() {} 6 | 7 | #[no_mangle] 8 | pub extern "C" fn alloc(size: usize) -> *mut c_void { 9 | let mut buf = Vec::with_capacity(size); 10 | let ptr = buf.as_mut_ptr(); 11 | mem::forget(buf); 12 | return ptr as *mut c_void; 13 | } 14 | 15 | #[no_mangle] 16 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) { 17 | unsafe { 18 | let _buf = Vec::from_raw_parts(ptr, 0, cap); 19 | } 20 | } 21 | 22 | #[no_mangle] 23 | pub extern "C" fn fill(pointer: *mut u8, max_width: usize, max_height: usize, time: f64) { 24 | 25 | // pixels are stored in RGBA, so each pixel is 4 bytes 26 | let byte_size = max_width * max_height * 4; 27 | let sl = unsafe { slice::from_raw_parts_mut(pointer, byte_size) }; 28 | 29 | for i in 0..byte_size { 30 | 31 | // get the position of current pixel 32 | let height = i / 4 / max_width; 33 | let width = i / 4 % max_width; 34 | 35 | if i%4 == 3 { 36 | 37 | // set opacity to 1 38 | sl[i] = 255; 39 | 40 | } else if i%4 == 0 { 41 | 42 | // create a red ripple effect from the top left corner 43 | let len = ((height*height + width*width) as f64).sqrt(); 44 | let nb = time + len / 4.0; 45 | let a = 128.0 + nb.cos() * 128.0; 46 | sl[i] = a as u8; 47 | 48 | 49 | } else if i % 4 == 2 { 50 | 51 | // create a blue ripple effect from the top right corner 52 | let width = 500 - width; 53 | let len = ((height*height + width*width) as f64).sqrt(); 54 | let nb = time + len / 4.0; 55 | let a = 128.0 + nb.cos() * 128.0; 56 | sl[i] = a as u8; 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /demos/factorial/Makefile: -------------------------------------------------------------------------------- 1 | NAME=factorial 2 | 3 | build: $(NAME).wasm $(NAME).wat 4 | 5 | %.wat: %.wasm 6 | wasm2wat $< > $@ 7 | 8 | %.wasm: %.big.wasm 9 | wasm-gc $< $@ 10 | 11 | %.big.wasm: %.rs 12 | rustc +nightly --target wasm32-unknown-unknown -O --crate-type=cdylib $< -o $@ 13 | -------------------------------------------------------------------------------- /demos/factorial/factorial.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::ffi::CString; 3 | use std::os::raw::{c_char, c_void}; 4 | 5 | #[no_mangle] 6 | pub extern "C" fn alloc(size: usize) -> *mut c_void { 7 | let mut buf = Vec::with_capacity(size); 8 | let ptr = buf.as_mut_ptr(); 9 | mem::forget(buf); 10 | return ptr as *mut c_void; 11 | } 12 | 13 | #[no_mangle] 14 | pub extern "C" fn dealloc_str(ptr: *mut c_char) { 15 | unsafe { 16 | let _ = CString::from_raw(ptr); 17 | } 18 | } 19 | 20 | #[no_mangle] 21 | pub extern "C" fn fact(n: u32) -> u64 { 22 | let mut n = n as u64; 23 | let mut result = 1; 24 | while n > 0 { 25 | result = result * n; 26 | n = n - 1; 27 | } 28 | result 29 | } 30 | 31 | #[no_mangle] 32 | pub extern "C" fn fact_str(n: u32) -> *mut c_char { 33 | let res = fact(n); 34 | let s = format!("{}", res); 35 | let s = CString::new(s).unwrap(); 36 | s.into_raw() 37 | } 38 | -------------------------------------------------------------------------------- /demos/factorial/factorial.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badboy/hellorust/f2dce7d25cd0a07b12a82c6738f205b1e67d8ee7/demos/factorial/factorial.wasm -------------------------------------------------------------------------------- /demos/factorial/index.liquid: -------------------------------------------------------------------------------- 1 | permalink: /demos/factorial 2 | title: "Demo: Factorial in WebAssembly" 3 | layout: site.liquid 4 | --- 5 |

Factorial in WebAssembly

6 | 7 |

8 | Type a number into the field and the factorial will be calculated. 9 | JavaScript has no plain integers, all numbers are actually 64-bit floats. 10 | It thus cannot correctly handle numbers bigger than 53 bits. 11 | However, in order to handle the factorial of up to 20, we need 64-bit integers. 12 | By doing the formatting in Rust (and thus WebAssembly) and returning a string, 13 | we can correctly print the result. 14 | For values larger than 20 the result is still incorrect. 15 |

16 | 17 |
18 | 19 |
20 |
21 | fact(_) = 22 | 1 23 | 24 | 25 | 26 | 58 | 59 |
60 | 61 |

The Code

62 | 63 |

64 | The Rust code implements a simple fact function 65 | as well as a function formatting the 64-bit integer to a string called fact_str. 66 |

67 |

68 | Download factorial.rs 69 |
70 | Download factorial.wat (WebAssembly text format) 71 |

72 | 73 |
fn main() {}
 74 | 
 75 | #[no_mangle]
 76 | pub extern "C" fn fact(mut n: u32) -> u64 {
 77 |     let n = n as u64;
 78 |     let mut result = 1;
 79 |     while n > 0 {
 80 |         result = result * n;
 81 |         n = n - 1;
 82 |     }
 83 |     result
 84 | }
 85 | 
 86 | #[no_mangle]
 87 | pub extern "C" fn fact_str(n: u32) -> *mut c_char {
 88 |     let res = fact(n);
 89 |     let s = format!("{}", res);
 90 |     let s = CString::new(s).unwrap();
 91 |     s.into_raw()
 92 | }
 93 | 
94 | 95 |

96 | The JavaScript code loads the WebAssembly module and has access to the exported function. 97 | However it can't call the fact function, 98 | as it 64-bit integers can't be passed back to JavaScript. 99 | We therefore have to call the formatting function instead and extract the string. 100 | The helper function copyCStr is defined in 101 | bundle.js. 102 |

103 | 104 |
window.Module = {};
105 | fetchAndInstantiate("./factorial.wasm", {})
106 | .then(mod => {
107 |   Module.fact  = mod.exports.fact;
108 |   Module.alloc = mod.exports.alloc;
109 |   Module.dealloc_str = mod.exports.dealloc_str;
110 |   Module.memory = mod.exports.memory;
111 |   Module.fact_str = function(n) {
112 |     let outptr = mod.exports.fact_str(n);
113 |     let result = copyCStr(Module, outptr);
114 |     return result;
115 |   };
116 | })
117 | 
118 | 119 |

120 | The compiled WebAssembly code is reasonably short. Try to understand what's happening! 121 |

122 | 123 |
(func $fact (param i32) (result i64)
124 |   (local i64 i64)
125 |   block  ;; label = @1
126 |     block  ;; label = @2
127 |       get_local 0
128 |       i32.eqz
129 |       br_if 0 (;@2;)
130 |       get_local 0
131 |       i64.extend_u/i32
132 |       set_local 1
133 |       i64.const 1
134 |       set_local 2
135 |       loop  ;; label = @3
136 |         get_local 1
137 |         get_local 2
138 |         i64.mul
139 |         set_local 2
140 |         get_local 1
141 |         i64.const -1
142 |         i64.add
143 |         tee_local 1
144 |         i64.eqz
145 |         i32.eqz
146 |         br_if 0 (;@3;)
147 |         br 2 (;@1;)
148 |       end
149 |       unreachable
150 |     end
151 |     i64.const 1
152 |     set_local 2
153 |   end
154 |   get_local 2)
155 | 
156 | -------------------------------------------------------------------------------- /demos/feistel/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "feistel" 3 | version = "0.1.0" 4 | 5 | -------------------------------------------------------------------------------- /demos/feistel/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "feistel" 3 | version = "0.1.0" 4 | 5 | [lib] 6 | path = "feistel.rs" 7 | crate-type = ["cdylib"] 8 | 9 | [dependencies] 10 | 11 | [profile.release] 12 | opt-level = "s" 13 | -------------------------------------------------------------------------------- /demos/feistel/Makefile: -------------------------------------------------------------------------------- 1 | NAME=feistel 2 | 3 | build: $(NAME).wasm $(NAME).wat 4 | 5 | %.wat: %.wasm 6 | wasm2wat $< > $@ 7 | 8 | %.wasm: %.big.wasm 9 | wasm-gc $< $@ 10 | 11 | %.big.wasm: %.rs 12 | cargo +nightly build --target wasm32-unknown-unknown --release 13 | cp ./target/wasm32-unknown-unknown/release/$(NAME).wasm $@ 14 | -------------------------------------------------------------------------------- /demos/feistel/feistel.rs: -------------------------------------------------------------------------------- 1 | use std::mem; 2 | use std::slice; 3 | use std::os::raw::c_void; 4 | 5 | #[no_mangle] 6 | pub extern "C" fn alloc(size: usize) -> *mut c_void { 7 | let mut buf = Vec::with_capacity(size); 8 | let ptr = buf.as_mut_ptr(); 9 | mem::forget(buf); 10 | return ptr as *mut c_void; 11 | } 12 | 13 | #[no_mangle] 14 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) { 15 | unsafe { 16 | let _buf = Vec::from_raw_parts(ptr, 0, cap); 17 | } 18 | } 19 | 20 | fn feistel_net(input: u16) -> u16 { 21 | let mut l = input & 0xff; 22 | let mut r = input >> 8; 23 | 24 | for _ in 0..8 { 25 | let nl = r; 26 | let f = (((r * 11) + (r >> 5) + 7 * 127) ^ r) & 0xff; 27 | r = l ^ f; 28 | l = nl; 29 | } 30 | 31 | ((r<<8)|l) & 0xffff 32 | } 33 | 34 | #[test] 35 | fn feistel_fills_all() { 36 | let mut v = vec![0u8; 65536]; 37 | let ones = vec![1u8; 65536]; 38 | 39 | for frame in 0..65536u32 { 40 | let point = feistel_net(frame as u16); 41 | v[point as usize] = 1; 42 | } 43 | 44 | assert_eq!(&ones[0..10], &v[0..10]); 45 | } 46 | 47 | fn set_pixel(pixels: &mut [u8], width: u16, x: u16, y: u16) { 48 | let x = x as usize; 49 | let y = y as usize; 50 | let width = width as usize; 51 | let offset = x*4 + y*4 * width; 52 | 53 | pixels[(offset+0)] = 255; 54 | pixels[(offset+1)] = 0; 55 | pixels[(offset+2)] = 0; 56 | pixels[(offset+3)] = 255; 57 | } 58 | 59 | #[no_mangle] 60 | pub extern "C" fn clear(pointer: *mut u8, width: usize, height: usize) { 61 | let byte_size = width * height * 4; 62 | let buf = unsafe { slice::from_raw_parts_mut(pointer, byte_size) }; 63 | 64 | for i in buf.iter_mut() { 65 | *i = 0; 66 | } 67 | } 68 | 69 | #[no_mangle] 70 | pub extern "C" fn fill(pointer: *mut u8, width: usize, height: usize, mut frame: u32) -> u32 { 71 | if frame == 65536 { 72 | return frame; 73 | } 74 | 75 | // pixels are stored in RGBA, so each pixel is 4 bytes 76 | let byte_size = width * height * 4; 77 | let buf = unsafe { slice::from_raw_parts_mut(pointer, byte_size) }; 78 | 79 | let width = width as u16; 80 | let height = height as u16; 81 | 82 | for _ in 0..200 { 83 | if frame == 65536 { 84 | break; 85 | } 86 | 87 | let pos = feistel_net(frame as u16); 88 | let x = pos % width; 89 | let y = pos / width; 90 | if x < width && y < height { 91 | set_pixel(buf, width, x, y); 92 | } 93 | 94 | frame += 1; 95 | } 96 | 97 | frame 98 | } 99 | -------------------------------------------------------------------------------- /demos/feistel/feistel.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badboy/hellorust/f2dce7d25cd0a07b12a82c6738f205b1e67d8ee7/demos/feistel/feistel.wasm -------------------------------------------------------------------------------- /demos/feistel/index.liquid: -------------------------------------------------------------------------------- 1 | permalink: /demos/feistel 2 | title: "Demo: FizzleFade effect using a Feistel network" 3 | layout: site.liquid 4 | --- 5 |

FizzleFade effect using a Feistel network

6 | 7 |

8 | based on code by Salvatore Sanfilippo (antirez). 9 |

10 | 11 | 12 |
13 |

14 | Caution: This will display a flickering image. 15 |

16 | 17 | 18 | 19 | 20 |
21 | 22 |

The Code

23 | 24 |

25 | This example shares a buffer between the Javascript side and the Rust side, 26 | and uses it to update the raw pixels on a canvas. 27 |

28 |

29 | Download feistel.rs 30 |
31 | Download feistel.wat (WebAssembly text format) 32 |

33 | 34 |
use std::mem;
 35 | use std::slice;
 36 | use std::os::raw::c_void;
 37 | 
 38 | #[no_mangle]
 39 | pub extern "C" fn alloc(size: usize) -> *mut c_void {
 40 |     let mut buf = Vec::with_capacity(size);
 41 |     let ptr = buf.as_mut_ptr();
 42 |     mem::forget(buf);
 43 |     return ptr as *mut c_void;
 44 | }
 45 | 
 46 | #[no_mangle]
 47 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
 48 |     unsafe  {
 49 |         let _buf = Vec::from_raw_parts(ptr, 0, cap);
 50 |     }
 51 | }
 52 | 
 53 | fn feistel_net(input: u16) -> u16 {
 54 |     let mut l = input & 0xff;
 55 |     let mut r = input >> 8;
 56 | 
 57 |     for _ in 0..8 {
 58 |         let nl = r;
 59 |         let f = (((r * 11) + (r >> 5) + 7 * 127) ^ r) & 0xff;
 60 |         r = l ^ f;
 61 |         l = nl;
 62 |     }
 63 | 
 64 |     ((r<<8)|l) & 0xffff
 65 | }
 66 | 
 67 | fn set_pixel(pixels: &mut [u8], width: u16, x: u16, y: u16) {
 68 |     let x = x as usize;
 69 |     let y = y as usize;
 70 |     let width = width as usize;
 71 |     let offset = x*4 + y*4 * width;
 72 | 
 73 |     pixels[(offset+0)] = 255;
 74 |     pixels[(offset+1)] = 0;
 75 |     pixels[(offset+2)] = 0;
 76 |     pixels[(offset+3)] = 255;
 77 | }
 78 | 
 79 | #[no_mangle]
 80 | pub extern "C" fn clear(pointer: *mut u8, width: usize, height: usize) {
 81 |     let byte_size = width * height * 4;
 82 |     let buf = unsafe { slice::from_raw_parts_mut(pointer, byte_size) };
 83 | 
 84 |     for i in buf.iter_mut() {
 85 |         *i = 0;
 86 |     }
 87 | }
 88 | 
 89 | #[no_mangle]
 90 | pub extern "C" fn fill(pointer: *mut u8, width: usize, height: usize, mut frame: u32) -> u32 {
 91 |     if frame == 65536 {
 92 |         return frame;
 93 |     }
 94 | 
 95 |     // pixels are stored in RGBA, so each pixel is 4 bytes
 96 |     let byte_size = width * height * 4;
 97 |     let buf = unsafe { slice::from_raw_parts_mut(pointer, byte_size) };
 98 | 
 99 |     let width = width as u16;
100 |     let height = height as u16;
101 | 
102 |     for _ in 0..200 {
103 |         if frame == 65536 {
104 |             break;
105 |         }
106 | 
107 |         let pos = feistel_net(frame as u16);
108 |         let x = pos % width;
109 |         let y = pos / width;
110 |         if x < width && y < height {
111 |             set_pixel(buf, width, x, y);
112 |         }
113 | 
114 |         frame += 1;
115 |     }
116 | 
117 |     frame
118 | }
119 | 
120 | 121 | 122 |

123 | The JavaScript code loads the WebAssembly module and has access to the exported functions and fields, including the allocation and memory. 124 | We create an ImageData backed by memory allocated on the Rust side, and regularly update it. 125 |

126 | 127 |
fetch("feistel.wasm", {cache: "no-cache"}).then(response =>
128 |   response.arrayBuffer()
129 | ).then(bytes =>
130 |   WebAssembly.instantiate(bytes, {})
131 | ).then(results => {
132 |   let module = {};
133 |   let mod = results.instance;
134 |   module.alloc   = mod.exports.alloc;
135 |   module.dealloc = mod.exports.dealloc;
136 |   module.fill    = mod.exports.fill;
137 |   module.clear   = mod.exports.clear;
138 | 
139 |   var width  = 320;
140 |   var height = 200;
141 | 
142 |   let byteSize = width * height * 4;
143 |   var pointer = module.alloc( byteSize );
144 |   var buffer = new Uint8Array(mod.exports.memory.buffer, pointer, byteSize);
145 | 
146 |   var button = document.getElementById("run-wasm");
147 |   var canvas = document.getElementById('screen');
148 |   if (canvas.getContext) {
149 |     var ctx = canvas.getContext('2d');
150 | 
151 |     var pointer = module.alloc( byteSize );
152 | 
153 |     var usub = new Uint8ClampedArray(mod.exports.memory.buffer, pointer, byteSize);
154 |     var img = new ImageData(usub, width, height);
155 |     var running = false;
156 | 
157 |     var frame = 0;
158 |     var running = false;
159 |     function step(timestamp) {
160 |       if (!running) return;
161 | 
162 |       frame = module.fill(pointer, width, height, frame);
163 |       ctx.putImageData(img, 0, 0)
164 | 
165 |       if (frame != 65536) {
166 |         window.requestAnimationFrame(step);
167 |       } else {
168 |         button.innerText = "Restart";
169 |         running = false;
170 |       }
171 |     }
172 | 
173 |     function clearCanvasAndRestart() {
174 |       running = false;
175 |       window.requestAnimationFrame(function() {
176 |         ctx.clearRect(0, 0, width, height);
177 |         module.clear(pointer, width, height);
178 |         frame = 0;
179 |         running = true;
180 |         window.requestAnimationFrame(step);
181 |       });
182 |     }
183 | 
184 |     button.addEventListener("click", function(e) {
185 |       if (running) {
186 |         button.innerText = "Restart";
187 |         running = false;;
188 |       } else {
189 |         button.innerText = "Stop";
190 |         clearCanvasAndRestart();
191 |       }
192 |     });
193 |   }
194 | });
195 | 
196 | -------------------------------------------------------------------------------- /demos/feistel/script.js: -------------------------------------------------------------------------------- 1 | fetch("feistel.wasm", {cache: "no-cache"}).then(response => 2 | response.arrayBuffer() 3 | ).then(bytes => 4 | WebAssembly.instantiate(bytes, {}) 5 | ).then(results => { 6 | let module = {}; 7 | let mod = results.instance; 8 | module.alloc = mod.exports.alloc; 9 | module.dealloc = mod.exports.dealloc; 10 | module.fill = mod.exports.fill; 11 | module.clear = mod.exports.clear; 12 | 13 | var width = 320; 14 | var height = 200; 15 | 16 | let byteSize = width * height * 4; 17 | var pointer = module.alloc( byteSize ); 18 | var buffer = new Uint8Array(mod.exports.memory.buffer, pointer, byteSize); 19 | 20 | var button = document.getElementById("run-wasm"); 21 | var canvas = document.getElementById('screen'); 22 | if (canvas.getContext) { 23 | var ctx = canvas.getContext('2d'); 24 | 25 | var pointer = module.alloc( byteSize ); 26 | 27 | var usub = new Uint8ClampedArray(mod.exports.memory.buffer, pointer, byteSize); 28 | var img = new ImageData(usub, width, height); 29 | var running = false; 30 | 31 | var frame = 0; 32 | var running = false; 33 | function step(timestamp) { 34 | if (!running) return; 35 | 36 | frame = module.fill(pointer, width, height, frame); 37 | ctx.putImageData(img, 0, 0) 38 | 39 | if (frame != 65536) { 40 | window.requestAnimationFrame(step); 41 | } else { 42 | button.innerText = "Restart"; 43 | running = false; 44 | } 45 | } 46 | 47 | function clearCanvasAndRestart() { 48 | running = false; 49 | window.requestAnimationFrame(function() { 50 | ctx.clearRect(0, 0, width, height); 51 | module.clear(pointer, width, height); 52 | frame = 0; 53 | running = true; 54 | window.requestAnimationFrame(step); 55 | }); 56 | } 57 | 58 | button.addEventListener("click", function(e) { 59 | if (running) { 60 | button.innerText = "Restart"; 61 | running = false;; 62 | } else { 63 | button.innerText = "Stop"; 64 | clearCanvasAndRestart(); 65 | } 66 | }); 67 | } 68 | }); 69 | -------------------------------------------------------------------------------- /demos/import-memory/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /demos/import-memory/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "string-passing" 3 | version = "0.1.0" 4 | 5 | -------------------------------------------------------------------------------- /demos/import-memory/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "string-passing" 3 | version = "0.1.0" 4 | authors = ["Jan-Erik Rediger "] 5 | 6 | [lib] 7 | path = "string-passing.rs" 8 | crate-type = ["cdylib"] 9 | 10 | [profile.release] 11 | opt-level = "s" 12 | -------------------------------------------------------------------------------- /demos/import-memory/Makefile: -------------------------------------------------------------------------------- 1 | NAME=string-passing 2 | 3 | build: $(NAME).wasm $(NAME).wat 4 | 5 | %.wat: %.wasm 6 | wasm2wat $< > $@ 7 | 8 | %.wasm: %.rs 9 | cargo +nightly build --release --target wasm32-unknown-unknown 10 | cp target/wasm32-unknown-unknown/release/string_passing.wasm $@ 11 | -------------------------------------------------------------------------------- /demos/import-memory/index.liquid: -------------------------------------------------------------------------------- 1 | permalink: "/demos/import-memory" 2 | title: "Demo: Import memory" 3 | layout: site.liquid 4 | --- 5 |

Import memory buffer

6 | 7 | 8 | 9 | 10 | 36 | 37 |
38 | 39 |

The Code

40 | 41 |

42 | Download string-passing.rs 43 |
44 | Download string-passing.wat (WebAssembly text format) 45 |

46 |

47 | The Rust code for this example does nothing but returning the data passed to it (plus some allocation functions). 48 | It uses one special attribute though: 49 | 50 |

#![wasm_import_memory]
51 | 52 | With this attribute the WebAssembly module will not export its own memory buffer, 53 | but instead import the memory from the environment, letting the caller set it up. 54 | In the future it will be possible to use a SharedArrayBuffer in order to share memory between multiple modules easily. 55 |

56 | 57 |

58 | The code (without allocation functions) is: 59 |

60 | 61 |
#![feature(wasm_import_memory)]
 62 | #![wasm_import_memory]
 63 | 
 64 | use std::os::raw::c_char;
 65 | 
 66 | #[no_mangle]
 67 | pub extern "C" fn roundtrip(data: *mut c_char) -> *mut c_char {
 68 |     data
 69 | }
70 | 71 |

72 | The calling JavaScript code needs to instantiate a 73 | WebAssembly.Memory object and pass it on instantiation. 74 |

75 | 76 |
window.Module = {}
 77 | 
 78 | // Initializing the memory with 20 pages (20 * 64KiB = 1.25 MiB)
 79 | const memory = new WebAssembly.Memory({initial: 20});
 80 | const imports = {
 81 |   env: {
 82 |     memory: memory
 83 |   }
 84 | };
 85 | 
 86 | // On instantiation we pass the imports object
 87 | fetchAndInstantiate("./string-passing.wasm", imports)
 88 |   .then(mod => {
 89 |     Module.memory      = memory;
 90 |     Module.alloc       = mod.exports.alloc;
 91 |     Module.dealloc     = mod.exports.dealloc;
 92 |     Module.dealloc_str = mod.exports.dealloc_str;
 93 |     Module.roundtrip   = function(str) {
 94 |       let buf = newString(Module, str);
 95 |       let outptr = mod.exports.roundtrip(buf);
 96 |       let result = copyCStr(Module, outptr);
 97 |       return result;
 98 |     };
 99 | 
100 |     var output = document.getElementById("output");
101 |     output.value = Module.roundtrip("This string was passed through WebAssembly")
102 |   });
103 | 
104 | -------------------------------------------------------------------------------- /demos/import-memory/string-passing.rs: -------------------------------------------------------------------------------- 1 | #![feature(wasm_import_memory)] 2 | #![wasm_import_memory] 3 | 4 | use std::mem; 5 | use std::ffi::CString; 6 | use std::os::raw::{c_char, c_void}; 7 | 8 | #[no_mangle] 9 | pub extern "C" fn alloc(size: usize) -> *mut c_void { 10 | let mut buf = Vec::with_capacity(size); 11 | let ptr = buf.as_mut_ptr(); 12 | mem::forget(buf); 13 | return ptr as *mut c_void; 14 | } 15 | 16 | #[no_mangle] 17 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) { 18 | unsafe { 19 | let _buf = Vec::from_raw_parts(ptr, 0, cap); 20 | } 21 | } 22 | 23 | #[no_mangle] 24 | pub extern "C" fn dealloc_str(ptr: *mut c_char) { 25 | unsafe { 26 | let _ = CString::from_raw(ptr); 27 | } 28 | } 29 | 30 | #[no_mangle] 31 | pub extern "C" fn roundtrip(data: *mut c_char) -> *mut c_char { 32 | data 33 | } 34 | -------------------------------------------------------------------------------- /demos/import-memory/string-passing.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badboy/hellorust/f2dce7d25cd0a07b12a82c6738f205b1e67d8ee7/demos/import-memory/string-passing.wasm -------------------------------------------------------------------------------- /demos/index.md: -------------------------------------------------------------------------------- 1 | permalink: /demos 2 | title: Demos 3 | layout: site.liquid 4 | data: 5 | route: demos 6 | --- 7 | ## Demos 8 | 9 | Some examples of Rust code compiled to JavaScript or WebAssembly. 10 | Code will be made published soon (but is more or less the default example with some JavaScript wrappers). 11 | 12 | * [Minimal working example](/demos/add/index.html) 13 | * [Calculate SHA1 digest](/demos/sha1/index.html) 14 | * [Factorial](/demos/factorial/index.html) 15 | * [Canvas](/demos/canvas/index.html) 16 | * [Import memory](/demos/import-memory/index.html) 17 | * [FizzleFade effect with a Feistel network](/demos/feistel/index.html) 18 | * [Call JavaScript from Rust](/demos/call-js/index.html) 19 | -------------------------------------------------------------------------------- /demos/sha1/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | -------------------------------------------------------------------------------- /demos/sha1/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "sha1" 3 | version = "0.2.0" 4 | source = "registry+https://github.com/rust-lang/crates.io-index" 5 | 6 | [[package]] 7 | name = "sha1-digest" 8 | version = "0.1.0" 9 | dependencies = [ 10 | "sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", 11 | ] 12 | 13 | [metadata] 14 | "checksum sha1 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cc30b1e1e8c40c121ca33b86c23308a090d19974ef001b4bf6e61fd1a0fb095c" 15 | -------------------------------------------------------------------------------- /demos/sha1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sha1-digest" 3 | version = "0.1.0" 4 | authors = ["Jan-Erik Rediger "] 5 | 6 | [lib] 7 | path = "sha1-digest.rs" 8 | crate-type = ["cdylib"] 9 | 10 | [dependencies] 11 | sha1 = "0.2.0" 12 | 13 | [profile.release] 14 | opt-level = "s" 15 | -------------------------------------------------------------------------------- /demos/sha1/Makefile: -------------------------------------------------------------------------------- 1 | build: sha1-digest.wasm sha1-digest.wat 2 | 3 | sha1-digest.wat: sha1-digest.wasm 4 | wasm2wat $< > $@ 5 | 6 | sha1-digest.wasm: sha1-digest.big.wasm 7 | wasm-gc $< $@ 8 | 9 | sha1-digest.big.wasm: sha1-digest.rs 10 | cargo +nightly build --target wasm32-unknown-unknown --release 11 | cp target/wasm32-unknown-unknown/release/sha1_digest.wasm $@ 12 | -------------------------------------------------------------------------------- /demos/sha1/index.liquid: -------------------------------------------------------------------------------- 1 | permalink: /demos/sha1 2 | title: "Demo: Calculate the SHA1 hash of input" 3 | layout: site.liquid 4 | --- 5 |

Calculate the SHA1 hash of input

6 | 7 |

8 | Type some text into the field and the SHA1 hash will be calculated. 9 |

10 | 11 |
12 | 13 |
14 |

Digest:

15 | Digest will be shown here 16 | 17 | 18 | 19 | 48 | 49 |
50 | 51 |

The Code

52 | 53 |

54 | The Rust code for this example is short and exposes a C-like API on top of the 55 | sha1 crate. 56 |

57 |

58 | Download sha1-digest.rs 59 |
60 | Download sha1-digest.wat (WebAssembly text format) 61 |

62 | 63 |
extern crate sha1;
 64 | 
 65 | use std::mem;
 66 | use std::ffi::{CString, CStr};
 67 | use std::os::raw::{c_char, c_void};
 68 | 
 69 | use sha1::Sha1;
 70 | 
 71 | // In order to work with the memory we expose (de)allocation methods
 72 | #[no_mangle]
 73 | pub extern "C" fn alloc(size: usize) -> *mut c_void {
 74 |     let mut buf = Vec::with_capacity(size);
 75 |     let ptr = buf.as_mut_ptr();
 76 |     mem::forget(buf);
 77 |     return ptr as *mut c_void;
 78 | }
 79 | 
 80 | #[no_mangle]
 81 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) {
 82 |     unsafe  {
 83 |         let _buf = Vec::from_raw_parts(ptr, 0, cap);
 84 |     }
 85 | }
 86 | 
 87 | // The JavaScript side passes a pointer to a C-like string that's already placed into memory.
 88 | // On the Rust side we turn this into a CStr, extract the bytes, pass it through the crate
 89 | // and then turn it back into an memory-allocated C-like string.
 90 | // A pointer to this data is returned.
 91 | #[no_mangle]
 92 | pub extern "C" fn digest(data: *mut c_char) -> *mut c_char {
 93 |     unsafe {
 94 |         let data = CStr::from_ptr(data);
 95 | 
 96 |         let mut m = Sha1::new();
 97 |         m.update(data.to_bytes());
 98 |         let dgst = m.digest().to_string();
 99 |         let s = CString::new(dgst).unwrap();
100 |         s.into_raw()
101 |     }
102 | }
103 | 
104 | 105 |

106 | This can be built using cargo with the command cargo +nightly build --target wasm32-unknown-unknown --release. 107 |

108 | 109 |

110 | The JavaScript code loads the WebAssembly module and has access to the exported functions and fields, including the allocation and memory. 111 | To pass a JavaScript string, we first allocate some space in the exported memory buffer and copy over the string. 112 | On return of the digest function, we copy back the new string from the memory buffer. 113 |

114 | 115 |
var Module = {}
116 | var Sha1 = {
117 |   digest: function(str) {
118 |     let buf = newString(Module, str);
119 |     let outptr = Module.digest(buf);
120 |     let result = copyCStr(Module, outptr);
121 |     Module.dealloc_str(buf);
122 |     return result;
123 |   }
124 | }
125 | 
126 | fetchAndInstantiate("./sha1-digest.wasm", {})
127 | .then(mod => {
128 |   Module.alloc   = mod.exports.alloc;
129 |   Module.dealloc = mod.exports.dealloc;
130 |   Module.digest  = mod.exports.digest;
131 |   Module.memory  = new Uint8Array(mod.exports.memory.buffer)
132 | 
133 |   var input = document.getElementById("input");
134 |   var output = document.getElementById("output");
135 |   input.addEventListener("keyup", function(e) {
136 |     output.innerText = Sha1.digest(input.value);
137 |   });
138 | });
139 | 
140 | -------------------------------------------------------------------------------- /demos/sha1/sha1-digest.rs: -------------------------------------------------------------------------------- 1 | extern crate sha1; 2 | 3 | use std::mem; 4 | use std::ffi::{CString, CStr}; 5 | use std::os::raw::{c_char, c_void}; 6 | 7 | use sha1::Sha1; 8 | 9 | #[no_mangle] 10 | pub extern "C" fn alloc(size: usize) -> *mut c_void { 11 | let mut buf = Vec::with_capacity(size); 12 | let ptr = buf.as_mut_ptr(); 13 | mem::forget(buf); 14 | return ptr as *mut c_void; 15 | } 16 | 17 | #[no_mangle] 18 | pub extern "C" fn dealloc(ptr: *mut c_void, cap: usize) { 19 | unsafe { 20 | let _buf = Vec::from_raw_parts(ptr, 0, cap); 21 | } 22 | } 23 | 24 | #[no_mangle] 25 | pub extern "C" fn dealloc_str(ptr: *mut c_char) { 26 | unsafe { 27 | let _ = CString::from_raw(ptr); 28 | } 29 | } 30 | 31 | #[no_mangle] 32 | pub extern "C" fn digest(data: *mut c_char) -> *mut c_char { 33 | unsafe { 34 | let data = CStr::from_ptr(data); 35 | 36 | let mut m = Sha1::new(); 37 | m.update(data.to_bytes()); 38 | let dgst = m.digest().to_string(); 39 | let s = CString::new(dgst).unwrap(); 40 | s.into_raw() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /demos/sha1/sha1-digest.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/badboy/hellorust/f2dce7d25cd0a07b12a82c6738f205b1e67d8ee7/demos/sha1/sha1-digest.wasm -------------------------------------------------------------------------------- /index.liquid: -------------------------------------------------------------------------------- 1 | title: Latest News 2 | layout: site.liquid 3 | --- 4 |

Latest News

5 | 6 | {% assign idx = 0 %} 7 | {% for post in collections.posts.pages %} 8 | {% if idx < 1 %} 9 |
10 |

{{ post.title }}

11 | 12 | - {{ post.data.author }} 13 |
14 |

{{ post.content | strip_html | truncatewords: 25, '...' }}

15 |
16 | {% endif %} 17 | {% assign idx = idx | plus: 1 %} 18 | {% endfor %} 19 | 20 | -------------------------------------------------------------------------------- /info.md: -------------------------------------------------------------------------------- 1 | permalink: /info 2 | title: Info 3 | layout: site.liquid 4 | data: 5 | route: info 6 | --- 7 | ## Info 8 | 9 | This site collects information, resources, articles, examples and links regarding Rust & WebAssembly. 10 | 11 | The source is available at [github.com/badboy/hellorust](https://github.com/badboy/hellorust). 12 | 13 | ## License [![CC0-badge]][CC0-deed] 14 | 15 | Texts and example code on [www.hellorust.com](https://www.hellorust.com/) are licensed under Creative Commons Zero v1.0 Universal License 16 | ([LICENSE-CC0](https://github.com/badboy/hellorust/blob/master/LICENSE-CC0) or ) 17 | 18 | Unless you explicitly state otherwise, any contribution intentionally submitted 19 | for inclusion in Hellorust by you, as defined in the CC0-1.0 license, shall be 20 | [dedicated to the public domain][CC0-deed] and licensed as above, without any additional 21 | terms or conditions. 22 | 23 | External dependencies used in example code remain under their own license. 24 | 25 | [CC0-deed]: https://creativecommons.org/publicdomain/zero/1.0/deed.en 26 | [CC0-badge]: /assets/cc-zero.svg 27 | -------------------------------------------------------------------------------- /news.liquid: -------------------------------------------------------------------------------- 1 | permalink: /news 2 | title: News 3 | layout: site.liquid 4 | data: 5 | route: news 6 | --- 7 |

News

8 | 9 | {% assign years = "2019, 2018, 2017" | split: ", " %} 10 | {% for year in years %} 11 | 12 |

{{year}}

13 | 14 |
    15 | {% for post in collections.posts.pages %} 16 | {% assign postyear = post.published_date | date: "%Y" %} 17 | {% if postyear == year %} 18 |
  • {{ post.published_date | date: "%d %b"}}: {{post.title}}
  • 19 | {% endif %} 20 | {% endfor %} 21 |
22 | {%endfor%} 23 | -------------------------------------------------------------------------------- /news/2017-12-03-canvas.md: -------------------------------------------------------------------------------- 1 | title: Writing to Canvas 2 | published_date: "2017-12-03 13:06:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: "Jan-Erik Rediger" 6 | --- 7 | [Geoffroy](https://github.com/Geal) added a new example: Generating image data displayed on a canvas. 8 | 9 | ## [Canvas example][canvas] 10 | 11 | *Caution: The example will display a flickering image after you press the start button.* 12 | 13 | See the included comments and documentation on how it works. 14 | 15 | [canvas]: /demos/canvas/index.html 16 | -------------------------------------------------------------------------------- /news/2017-12-04-brainfuck-interpreter.md: -------------------------------------------------------------------------------- 1 | title: "Link: Writing a Brainfuck Interpreter in Rust (and WebAssebmly)" 2 | published_date: "2017-12-04 12:18:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: "Jan-Erik Rediger" 6 | --- 7 | [Shritesh Bhattarai](https://shr.ite.sh/about) wrote a blog post about a simple Brainfuck interpreter written in Rust, compiled to WebAssembly: 8 | 9 | 10 | ## [Writing a Brainfuck Interpreter in Rust (and WebAssembly)](https://shr.ite.sh/brainfuck-rust-wasm.html) 11 | 12 | The source is available on GitHub: [github.com/shritesh/brainfuck-rs-wasm](https://github.com/shritesh/brainfuck-rs-wasm) and a live version [available online](http://shritesh.github.io/brainfuck-rs-wasm/). 13 | -------------------------------------------------------------------------------- /news/2017-12-04-rocket-a-game-on-wasm.md: -------------------------------------------------------------------------------- 1 | title: "Link: Rocket - A Rust game running on Wasm" 2 | published_date: "2017-12-04 09:49:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: "Jan-Erik Rediger" 6 | --- 7 | [Adolfo Ochagavía](https://aochagavia.github.io/about/) adapted his game [Rocket](https://github.com/aochagavia/rocket) to WebAssembly to run in the browser. 8 | 9 | Read how he did it: 10 | 11 | ## [Rocket - A Rust game running on WASM](https://aochagavia.github.io/blog/rocket---a-rust-game-running-on-wasm/) 12 | 13 | The post includes the game running right there in the browser. 14 | -------------------------------------------------------------------------------- /news/2017-12-05-semver-with-rust-and-wasm.md: -------------------------------------------------------------------------------- 1 | title: "Link: Semver on the web with Rust and WebAssembly" 2 | published_date: "2017-12-05 14:22:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: "Jan-Erik Rediger" 6 | --- 7 | [Steve Klabnik](https://github.com/steveklabnik/) built a Rust-powered minimal clone of the [npm semver calculator](https://semver.npmjs.com/) using Rust and WebAssembly. 8 | 9 | 10 | ## [Semver on the web with Rust and WebAssembly](https://www.steveklabnik.com/wasm/demos/semver.html) 11 | 12 | It has less features, but otherwise fully working. 13 | -------------------------------------------------------------------------------- /news/2017-12-06-the-case-for-wasm.md: -------------------------------------------------------------------------------- 1 | title: "Link: Rust and the case for WebAssembly in 2018" 2 | published_date: "2017-12-06 10:55:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: "Jan-Erik Rediger" 6 | --- 7 | [Michael Gattozzi](https://mgattozzi.com/about) discusses WebAssembly and what it can mean for Rust. 8 | He proposes some possible short-term, medium-term and long-term goals for the Rust 2018 roadmap. 9 | 10 | 11 | ## [Rust and the case for WebAssembly in 2018](https://mgattozzi.com/rust-wasm) 12 | 13 | The future of Rust & WebAssembly definitely needs a lot of work, but we're off to a good start to make Rust the language of choice here. 14 | -------------------------------------------------------------------------------- /news/2017-12-07-import-memory.md: -------------------------------------------------------------------------------- 1 | title: Importing memory 2 | published_date: "2017-12-07 15:32:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: "Jan-Erik Rediger" 6 | --- 7 | I added a new small example demonstrating how to instantiate a memory buffer from the JavaScript side 8 | and import it into the WebAssembly module. 9 | 10 | 11 | ## [Import memory](/demos/import-memory/index.html) 12 | 13 | I also added another canvas demo showing the [FizzleFade effect with a Feistel network](/demos/feistel/index.html). 14 | -------------------------------------------------------------------------------- /news/2017-12-14-chip8-emulator-on-wasm.md: -------------------------------------------------------------------------------- 1 | title: "Link: Wasm Rust CHIP8 - A CHIP8 emulator written in Rust running Wasm" 2 | published_date: "2017-12-14 17:22:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: Mark Sta Ana 6 | --- 7 | [Colin Eberhardt](http://blog.scottlogic.com/ceberhardt/) has written a [CHIP-8](https://colineberhardt.github.io/wasm-rust-chip8/web/) emulator in Rust and targeted it for WebAssembly. 8 | 9 | Read how he did it: 10 | 11 | ## [Writing a CHIP-8 emulator with Rust and WebAssembly](http://blog.scottlogic.com/2017/12/13/chip8-emulator-webassembly-rust.html) 12 | 13 | The post contains a detailed break down of how to write an emulator, along with the challenges of sharing data between Rust and WebAssembly. 14 | -------------------------------------------------------------------------------- /news/2018-01-08-new-years-roundup.md: -------------------------------------------------------------------------------- 1 | title: "New Year's Roundup" 2 | published_date: "2018-01-08 11:45:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: Jan-Erik Rediger 6 | --- 7 | 8 | The last post on here was a while back. Since then a lot of things happened in the Rust and WebAssembly world, so here's a quick roundup to get up to speed. 9 | 10 | ## [YEW - a framework for client-side web-apps](https://github.com/DenisKolodin/yew) 11 | 12 | Inspired by Elm and ReactJS it has a MVC approach, a virtual DOM, HTML templates with Rust inside and data conversions built-in. 13 | And apparently [it's fast as well as benchmarks show](https://github.com/DenisKolodin/todomvc-perf-comparison). 14 | 15 | ## Support for Rust/Wasm in Parcel 16 | 17 | Two weeks ago [initial support for Rust and Wasm](https://github.com/parcel-bundler/parcel/pull/312) landed in Parcel, a web application bundler. 18 | It can automatically (re-)compile your Rust sources when they change and import the resulting Wasm module in your JavaScript code. 19 | 20 | ## [wasm-bindgen](https://github.com/alexcrichton/wasm-bindgen) 21 | 22 | A CLI and Rust dependency that will generate the necessary JavaScript bindings to your Rust code, so you can skip writing most of the boilerplate (boilerplate such as [wasm-experiments](https://github.com/killercup/wasm-experiments)). 23 | It generates TypeScript for now, which gives you proper types on the JavaScript side as well. 24 | 25 | ## stdweb and cargo-web 26 | 27 | [stdweb](https://github.com/koute/stdweb/), "a standard library for the client-side Web" gained support for `wasm32-unknown-unknown` in December, 28 | including a `js!` macro to call into JavaScript functions from your Rust code. 29 | In combination with [cargo-web](https://github.com/koute/cargo-web), a cargo subcommand taking care of the compile step, this allows for powerful new web applications implemented in Rust. 30 | The above mentioned YEW is built on top of stdweb. 31 | 32 | ## More 33 | 34 | People are playing around with the new small WebAssembly target more and more. 35 | 36 | * [Porting a roguelike game to WebAssembly.](https://aimlesslygoingforward.com/blog/2017/12/25/dose-response-ported-to-webassembly/) - A detailed post on what it took to port the game to the web browser 37 | * [Try to make gluon compile compile to WASM](https://github.com/gluon-lang/gluon/issues/424) - Gluon is a embeddable language written in Rust 38 | * [Implement extensible syscall interface for wasm](https://github.com/rust-lang/rust/pull/47102) - An attempt to introduce a syscall interface in order to interact with the the runtime environment 39 | 40 | 41 | **You did something with Rust and WebAssembly? [Let us know!](https://github.com/badboy/hellorust/issues/new)** 42 | -------------------------------------------------------------------------------- /news/2018-01-09-turtles-on-wasm.md: -------------------------------------------------------------------------------- 1 | title: "Turtles on Wasm" 2 | published_date: "2018-01-09 16:23:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: Jan-Erik Rediger 6 | --- 7 | 8 | Marshall Pierce recently [added WebAssembly support](https://github.com/sunjay/turtle/pull/53) to the Turtle application. 9 | [Turtle](http://turtle.rs/) is a programming environment allowing to create animated drawings with a handful of simple commands. 10 | It's the perfect tool to teach programming and foster creativity. Bringing it to the web makes it even more accessible. 11 | He wrote down his approach for adding Wasm support: 12 | 13 | ## [Rust and WebAssembly With Turtle](https://varblog.org/blog/2018/01/08/rust-and-webassembly-with-turtle/) 14 | 15 | With a bit more effort it might be possible to provide a [Playground](https://play.rust-lang.org/)-like environment to run and code in the browser. 16 | -------------------------------------------------------------------------------- /news/2018-01-19-wasm-news.md: -------------------------------------------------------------------------------- 1 | title: "More News on WebAssembly" 2 | published_date: "2018-01-19 13:00:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: Jan-Erik Rediger 6 | --- 7 | 8 | Another week has passed and work on WebAssembly, documentation, tooling and usage is not stopping. 9 | Here's a recap of what happened recently: 10 | 11 | ### [Oxidizing Source Maps with Rust and WebAssembly](https://hacks.mozilla.org/2018/01/oxidizing-source-maps-with-rust-and-webassembly/) 12 | 13 | [Nick Fitzgerald](http://fitzgeraldnick.com/) and [Tom Tromey](http://tromey.com/) rewrote one of the most performance-sensitive code paths for source map parsing, 14 | that's used in JavaScript debuggers and other applications. 15 | Using Rust compiled to WebAssembly they were able to run more than 5 times faster for real-world use cases. 16 | All work is based on the `wasm32-unknown-unkown` target of Rust. 17 | Along the way they discovered a few shortcomings of this target and some bugs as well. 18 | 19 | 20 | ### [Making WebAssembly even faster: Firefox’s new streaming and tiering compiler](https://hacks.mozilla.org/2018/01/making-webassembly-even-faster-firefoxs-new-streaming-and-tiering-compiler/) 21 | 22 | [Lin Clark](https://code-cartoons.com) wrote another illustrated post on WebAssembly, this time introducing the new streaming compilation feature coming to Firefox 58. 23 | With this, WebAssembly can be compiled while it is streamed over the network and it will be ready to execute as soon as all the code is there. 24 | In combination with the baseline compiler in Firefox this brings another boost to running WebAssembly. 25 | 26 | ### Tooling 27 | 28 | Useful new tools came out of Nick's and Tom's work as well. 29 | One of these is [**wasm-snip**](https://github.com/fitzgen/wasm-snip), a tool to replace unused functions, that neither the compiler nor wasm-gc were able to remove with no-ops. 30 | This should get rid of some of the code bloat still in the module. 31 | Once we get a proper linker and improved compiler this will hopefully be unnecessary. 32 | 33 | [emk](https://github.com/emk) started a new tool to analyze size of WebAssembly modules more easily: [**wasm-bloat**](https://github.com/emk/wasm-bloat). 34 | 35 | If you're compiling to WebAssembly also take a look at [binaryen](https://github.com/WebAssembly/binaryen), 36 | which has tools like **wasm-opt** for even more optimization. 37 | 38 | ### [A point of coordination for all things rust+wasm](https://github.com/aturon/rust-wasm/) 39 | 40 | [Aaron](https://github.com/aturon) started a new repository to coordinate the development of Rust and its Wasm backend. 41 | With already [two dozen issues](https://github.com/aturon/rust-wasm/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc) opened, there's a lot going on. Jump into the discussion and shape how Rust and WebAssembly work together. 42 | 43 | ### More examples 44 | 45 | [Daniel](https://github.com/danielpclark) pointed me to two new demos of Rust and WebAssembly. 46 | 47 | [**wasm-astar**](https://github.com/jakedeichert/wasm-astar) is a A\* Pathfinding example, showed on an interactive map and some writing about Jake's experience working on it. 48 | 49 | [Koute](https://github.com/koute) wrote [**pinky**](https://github.com/koute/pinky), a fully working NES emulator written in Rust. 50 | Most excitingly there's also a Web frontend, running WebAssembly, to play your favourite game right in the browser: [**pinky-web**](https://github.com/koute/pinky/tree/master/pinky-web). 51 | 52 | 53 | --- 54 | 55 | Do **you** have another cool thing showing Rust & WebAssembly? 56 | Let me know [by opening an issue](https://github.com/badboy/hellorust/issues/new). 57 | -------------------------------------------------------------------------------- /news/2019-01-22-deprecation.md: -------------------------------------------------------------------------------- 1 | title: "Site deprecation, check out RustWasm" 2 | published_date: "2019-01-22 14:50:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: Jan-Erik Rediger 6 | --- 7 | 8 | This site hasn't been updated in over a year. 9 | Since then a lot of stuff happened in Rust and the WebAssembly world. 10 | 11 | The [Rust and WebAssembly working group](https://rustwasm.github.io/) was founded and now provides a lot of tooling, documentation, tutorials and other material. 12 | For a complete introduction to Rust and WebAssembly check out the [Rust 🦀 and WebAssembly 🕸 book](https://rustwasm.github.io/book/). 13 | -------------------------------------------------------------------------------- /news/big-factorial.md: -------------------------------------------------------------------------------- 1 | title: "Factorial with 64-bit integers" 2 | published_date: "2017-11-28 12:04:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: "Jan-Erik Rediger" 6 | --- 7 | I rewrote the [factorial][] example to use 64-bit integers 8 | and do the formatting in Rust (and thus WebAssembly), so larger values are displayed correctly. 9 | 10 | ## [Factorial example][factorial] 11 | 12 | (This also adds proper deallocation of the formatted strings) 13 | 14 | [factorial]: /demos/factorial/index.html 15 | -------------------------------------------------------------------------------- /news/minimal-example.md: -------------------------------------------------------------------------------- 1 | title: "Minimal Rust & WebAssembly example" 2 | published_date: "2017-11-26 16:25:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: "Jan-Erik Rediger" 6 | --- 7 | You can now find a minimal working example for compiling Rust to WebAssembly 8 | and runnig it in the browser in the demos section: 9 | 10 | ## [Minimal working example](/demos/add/index.html) 11 | 12 | The resulting WebAssembly modules is only 108 bytes big. Consider _that_ minimal. 13 | -------------------------------------------------------------------------------- /news/native-wasm-target.md: -------------------------------------------------------------------------------- 1 | title: "wasm32-unknown-unknown landed & enabled" 2 | published_date: "2017-11-26 12:24:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: "Jan-Erik Rediger" 6 | --- 7 | Yesterday the 8 | [Pull Request 46115: "rustbuild: Enable WebAssembly backend by default"](https://github.com/rust-lang/rust/pull/46115) was merged into Rust master. 9 | With the Nightly build from that night, the wasm32-unknown-unknown target is natively available. 10 | 11 | Once you install that nightly (or any later one from now on), you can compile to WebAssembly without additional tools: 12 | 13 | ``` 14 | rustup update 15 | rustup target add wasm32-unknown-unknown --toolchain nightly 16 | rustc +nightly --target wasm32-unknown-unknown -O hello.rs 17 | ``` 18 | 19 | Support & documentation is still a bit limited, but we're working to expand in this area. 20 | The Rust compiler also does not have a proper linker just now, so final WebAssembly modules will be quite big. 21 | Alex wrote a small tool to reduce the size: 22 | 23 | ``` 24 | cargo install --git https://github.com/alexcrichton/wasm-gc 25 | wasm-gc hello.wasm small-hello.wasm 26 | ``` 27 | 28 | You can find the full instruction [in the `wasm-32-unknown-unknown` setup guide](/setup/wasm-target). 29 | 30 | If you want to contribute with examples, documentation, articles or other resources open an issue or pull request on [github.com/badboy/hellorust](https://github.com/badboy/hellorust). 31 | -------------------------------------------------------------------------------- /news/post-1.md: -------------------------------------------------------------------------------- 1 | title: "Hello, Rust! Hello, WebAssembly!" 2 | published_date: "2017-11-18 15:10:00 +0100" 3 | layout: post.liquid 4 | data: 5 | author: "Jan-Erik Rediger" 6 | --- 7 | [hellorust.com](/) is live! 8 | 9 | This site will collect resources, guides, articles and links around Rust & WebAssembly. 10 | If you want to help or have more resources we should add, [open an issue](https://github.com/badboy/hellorust/issues/new) or send a Pull Request to [github.com/badboy/hellorust](https://github.com/badboy/hellorust). 11 | 12 | To get started, head over to the [Setup section](/setup/) or take a look at [some example code](/demos/). 13 | -------------------------------------------------------------------------------- /resources.md: -------------------------------------------------------------------------------- 1 | permalink: /resources 2 | title: Resources 3 | layout: site.liquid 4 | data: 5 | route: resources 6 | --- 7 | ## Resources 8 | 9 | A collection of various resources and blog posts explaining Rust & WebAssembly, available tools or showing demo applications. 10 | You have somethings that's missing from this list? [Tell us about it!](https://github.com/badboy/hellorust/issues/new) 11 | 12 | --- 13 | 14 | ## Blog posts / Discussions 15 | 16 | * [State of WebAssembly and Rust?](https://internals.rust-lang.org/t/state-of-webassembly-and-rust/6077/51) (2017-10-19) 17 | Discussion on the current state with lots of answers from people using it 18 | * [The Path to Rust on the Web](http://asquera.de/blog/2017-04-10/the-path-to-rust-on-the-web/) (2017-04-10) 19 | Article by Andrew Hobden 20 | 21 | 22 | ## Projects 23 | 24 | * [Wargo](https://github.com/lord/wargo) - Easy Rust to WebAssembly 25 | * [stdweb](https://github.com/koute/stdweb) - A standard library for the client-side Web 26 | * [cargo-web](https://github.com/koute/cargo-web) - A Cargo subcommand for the client-side Web 27 | 28 | 29 | ## Relevant issues on the Rust issue tracker 30 | 31 | * [#36317: Initial webassembly support via LLVM](https://github.com/rust-lang/rust/issues/36317) 32 | Tracking issue for getting WASM support into rustc 33 | * [#45905: Add a new wasm32-unknown-unknown target](https://github.com/rust-lang/rust/pull/45905) 34 | A new Rust compile target for WebAssembly without Emscripten 35 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | # empty on purpose 2 | -------------------------------------------------------------------------------- /setup/docker.md: -------------------------------------------------------------------------------- 1 | permalink: /setup/docker 2 | title: "Setup - Docker" 3 | layout: site.liquid 4 | --- 5 | ## Setup 6 | 7 | Pull down tomaka's Docker image: 8 | 9 | ``` 10 | docker pull tomaka/rustc-emscripten 11 | ``` 12 | 13 | Start a shell and mount the current directory into it: 14 | 15 | ``` 16 | docker run -it -v $(pwd):/src --rm tomaka/rustc-emscripten /bin/bash 17 | ``` 18 | 19 | Your code is now accessible in `/src` and can be compiled from there: 20 | 21 | ``` 22 | cd /src 23 | rustc --target wasm32-unknown-emscripten hello.rs 24 | ``` 25 | -------------------------------------------------------------------------------- /setup/emscripten.md: -------------------------------------------------------------------------------- 1 | permalink: /setup/emscripten 2 | title: "Setup - Emscripten" 3 | layout: site.liquid 4 | --- 5 | ## Setup 6 | 7 | To get a `rustc` with Emscripten support all you need is a recent version. 8 | You can install it using [rustup](http://rustup.rs/). Follow these steps: 9 | 10 | ``` 11 | rustup toolchain add stable 12 | rustup target add asmjs-unknown-emscripten --toolchain stable 13 | rustup target add wasm32-unknown-emscripten --toolchain stable 14 | ``` 15 | 16 | You still need the Emscripten SDK, see how to download and install it in detail or follow the simplified steps below (please refer to the official documentation for requirements and installation on Windows): 17 | 18 | ``` 19 | wget https://s3.amazonaws.com/mozilla-games/emscripten/releases/emsdk-portable.tar.gz 20 | tar -xvf emsdk-portable.tar.gz 21 | cd emsdk_portable 22 | ./emsdk update 23 | ./emsdk install sdk-incoming-64bit 24 | ``` 25 | 26 | Once everything is set up, you can compile with Emscripten: 27 | 28 | ``` 29 | rustc --target asmjs-unknown-emscripten hello.rs 30 | ``` 31 | 32 | You can also compile to WebAssembly instead: 33 | 34 | ``` 35 | rustc --target wasm32-unknown-emscripten hello.rs 36 | ``` 37 | -------------------------------------------------------------------------------- /setup/index.md: -------------------------------------------------------------------------------- 1 | permalink: /setup/ 2 | title: Setup 3 | layout: site.liquid 4 | data: 5 | route: setup 6 | --- 7 | ## Setup 8 | 9 | * [Setup `wasm32-unknown-unknown`](wasm-target/) *(recommended way, but more barebones)* 10 | * [Setup with Emscripten](emscripten/) 11 | * [Setup with Docker](docker/) 12 | -------------------------------------------------------------------------------- /setup/wasm-target.md: -------------------------------------------------------------------------------- 1 | permalink: "/setup/wasm-target" 2 | title: "Setup - Wasm target" 3 | layout: site.liquid 4 | --- 5 | ## Setup 6 | 7 | 15 | 16 | 24 | 25 | Install the latest nightly (2017-11-25 or later): 26 | 27 | ``` 28 | rustup toolchain install nightly 29 | ``` 30 | 31 | If you already installed `nightly` before, make sure it is up to date: 32 | 33 | ``` 34 | rustup update 35 | ``` 36 | 37 | Install the required target: 38 | 39 | ``` 40 | rustup target add wasm32-unknown-unknown --toolchain nightly 41 | ``` 42 | 43 | Compile your code to WebAssembly: 44 | 45 | ``` 46 | rustc +nightly --target wasm32-unknown-unknown -O hello.rs 47 | ``` 48 | 49 | (The `+nightly` part is a shortcut handled by rustup's wrappers around `rustc` and `cargo`) 50 | 51 | You will end up with a `hello.wasm`. It will be quite big. To reduce size, first install `wasm-gc`: 52 | 53 | ``` 54 | cargo install --git https://github.com/alexcrichton/wasm-gc 55 | ``` 56 | 57 | Then run it to reduce the size of the WebAssembly module: 58 | 59 | ``` 60 | wasm-gc hello.wasm small-hello.wasm 61 | ``` 62 | 63 | You can also compile a project to WebAssembly using `cargo`. 64 | First create the project: 65 | 66 | ``` 67 | cargo new myproject 68 | ``` 69 | 70 | Next, change the crate type to `cdylib`. Add this to your `Cargo.toml`: 71 | 72 | ``` 73 | [lib] 74 | path = "src/lib.rs" 75 | crate-type = ["cdylib"] 76 | ``` 77 | 78 | Finally, compile it: 79 | 80 | ``` 81 | cargo +nightly build --target wasm32-unknown-unknown --release 82 | ``` 83 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 3 | margin-top: 20px; 4 | margin-bottom: 10px; 5 | max-width: 820px; 6 | } 7 | p { 8 | margin-top: 1.2em; 9 | margin-bottom: 1.2em; 10 | } 11 | .pitch b { 12 | font-weight: 400; 13 | } 14 | li { 15 | font-size:1.25em; 16 | } 17 | h1 { 18 | font-size: 2em; 19 | margin-top: 1.2em; 20 | margin-bottom: 0.5em; 21 | } 22 | 23 | ul { 24 | padding-left: 30px; 25 | } 26 | ul.menu { 27 | font-size: 1em; 28 | margin: 0; 29 | padding: 0; 30 | text-align: center; 31 | } 32 | ul.menu li { 33 | list-style-type: none; 34 | margin-top: 0em; 35 | margin-bottom: 0em; 36 | } 37 | ul.menu h2 { 38 | font-size: 20px; 39 | font-weight: 500; 40 | margin: 1em; 41 | display: inline; 42 | line-height: 1.5em; 43 | } 44 | ul.menu li>ul { 45 | margin-top: .5em; 46 | padding-left: 0em; 47 | line-height: 1.5em; 48 | } 49 | 50 | .menu img { 51 | margin: 0 auto 20px auto; 52 | } 53 | @media (min-width: 992px) { 54 | ul.menu li.menu { 55 | margin-top: 3em; 56 | } 57 | ul.menu { 58 | text-align: left; 59 | } 60 | .menu img { 61 | margin: 0; 62 | } 63 | } 64 | h1 { 65 | font-weight: 500; 66 | font-size: 2em; /* gridfit */ 67 | } 68 | h2 { 69 | font-weight: 500; 70 | font-size: 1.75em; /* gridfit */ 71 | } 72 | h3 { 73 | font-weight: 500; 74 | } 75 | 76 | .table-features { 77 | width: 100%; 78 | margin-bottom: 18px; 79 | border: 1px solid #ccc; 80 | border-radius: 3px; 81 | border-collapse: inherit; 82 | } 83 | .table-features td { 84 | padding: 6px 12px; 85 | border: none; 86 | } 87 | 88 | div.install { 89 | margin-top: 2.5em; 90 | margin-bottom: 1.5em; 91 | } 92 | 93 | .table-installers { 94 | border-spacing: 4px; 95 | border: 0px; 96 | empty-cells: hide; 97 | } 98 | 99 | .table-installers td { 100 | padding: 0px; 101 | } 102 | 103 | .table-installers td.inst-type { 104 | border: 0px; 105 | color: black; 106 | text-align: left; 107 | width: 13em; 108 | } 109 | 110 | .table-installers div.inst-button { 111 | border: 1px solid #ccc; 112 | text-align: center; 113 | border-radius: 3px; 114 | padding: 0.3em; 115 | } 116 | 117 | .table-installers div.inst-button:hover { 118 | border-color: #428BCA; 119 | color: white; 120 | background-color: #428BCA; 121 | } 122 | 123 | .table-installers a { 124 | text-decoration: none; 125 | } 126 | 127 | .install-row { 128 | margin-bottom: 0em; 129 | } 130 | 131 | p.pitch { 132 | font-size: 25px; 133 | font-weight: 300; 134 | text-align: center; 135 | } 136 | @media (min-width: 992px) { 137 | p.pitch { 138 | font-size: 25px; 139 | margin-top: 1.5em; 140 | margin-bottom: 1em; 141 | margin-right: 1em; 142 | text-align: left; 143 | } 144 | } 145 | 146 | p.pitch a { 147 | font-size: 80%; 148 | } 149 | 150 | .install-box { 151 | color: #777; 152 | text-align: right; 153 | font-size: 130%; 154 | margin-top: 0.8em; 155 | } 156 | 157 | .version-rec-box-inner { 158 | display: block; 159 | font-size: 12px; 160 | text-align: center; 161 | } 162 | 163 | hr { 164 | margin-top: 2em; 165 | margin-bottom: 3em; 166 | border-top: 2px solid #dedede; 167 | } 168 | 169 | .asterisk { 170 | margin-left: 0px; 171 | color: #428BCA; 172 | } 173 | 174 | .footnote { 175 | color: #777; 176 | text-align: right; 177 | font-size: 12px; 178 | padding: 0 15px; 179 | } 180 | 181 | .laundry-list { 182 | font-size: 16px; 183 | list-style-type: square; 184 | } 185 | 186 | .install-box a.btn { 187 | display: block; 188 | margin:1em auto; 189 | max-width: 250px; 190 | } 191 | 192 | ul.laundry-list { 193 | column-count: 2; 194 | -moz-column-count: 2; 195 | -webkit-column-count: 2; 196 | margin-bottom: 20px; 197 | } 198 | 199 | .resp-block { 200 | display: none; 201 | } 202 | 203 | @media (min-width: 992px) { 204 | .install-box a.btn { 205 | font-size: 18px; 206 | padding: 10px 15px; 207 | } 208 | ul.laundry-list { 209 | column-count: auto; 210 | -moz-column-count: auto; 211 | -webkit-column-count: auto; 212 | } 213 | img.ribbon { 214 | display: inline !important; 215 | position: absolute; 216 | top: 0; 217 | right: 0; 218 | border: 0; 219 | width: 149px; 220 | height: 149px; 221 | } 222 | ul.menu .col-xs-12 { 223 | padding-left: 0; 224 | } 225 | .resp-block { 226 | display: block; 227 | } 228 | } 229 | 230 | #editor { 231 | padding: none; 232 | margin: none; 233 | width: 100%; 234 | min-height: 340px; 235 | font-size: 13px; 236 | font-family: Menlo, Monaco, Consolas, "Courier New", monospace; 237 | white-space: pre-wrap; 238 | } 239 | 240 | #active-code { 241 | position: relative; 242 | display: none; 243 | padding: 10px; 244 | border-radius: 4px; 245 | background-color: #FDFDFD; 246 | border: 1px solid #CCC; 247 | } 248 | 249 | #run-code { 250 | position: absolute; 251 | z-index: 10; 252 | float: right; 253 | right: 8px; 254 | top: 8px; 255 | outline: none; 256 | } 257 | 258 | .icon-link-ext:before { content: '\e800'; } /* 'î €' */ 259 | 260 | #result { 261 | background-color: #E2EEF6; 262 | margin-top: 10px; 263 | padding: 10px; 264 | display: none; 265 | border-radius: 4px; 266 | } 267 | 268 | .ace-error-text, .ace-error-line, .ace-warning-text, .ace-warning-line { 269 | position: absolute; 270 | } 271 | 272 | .ace-error-text { 273 | background-color: #e9abab; 274 | } 275 | 276 | .ace-error-line { 277 | background-color: #F6E2E2; 278 | } 279 | 280 | .ace-warning-text { 281 | background-color: #FFEF00; 282 | } 283 | 284 | .ace-warning-line { 285 | background-color: #FFFBCB; 286 | } 287 | 288 | .ace-chrome .ace_comment { 289 | color: #565656; 290 | } 291 | 292 | .ace-chrome .ace-keyword { 293 | color: #8959A8; 294 | } 295 | 296 | .ace-chrome .ace_entity.ace_name.ace_function { 297 | color: #4271AE; 298 | } 299 | 300 | .ace-chrome .ace_constant.ace_numeric { 301 | color: #718C00; 302 | } 303 | 304 | .ace-chrome .ace_keyword.ace_operator { 305 | color: black; 306 | } 307 | 308 | .ace-chrome .ace_string { 309 | color: #3E999F; 310 | } 311 | 312 | #active-code, #editor { 313 | background-color: #FAFAFA; 314 | } 315 | 316 | .more-examples { 317 | text-align: right; 318 | margin: 5px 10px 0 0; 319 | } 320 | 321 | .content { 322 | border-top: 2px solid #dedede; 323 | margin-top: 2em; 324 | margin-bottom: 8em; 325 | padding-top: 2em; 326 | } 327 | 328 | .content p, 329 | .content ul, 330 | .content ol, 331 | .content blockquote, 332 | .content pre { 333 | margin: 0 0 1rem; 334 | } 335 | 336 | .content h1 { 337 | font-size: 2.5em; 338 | line-height: 1.5em; 339 | margin-top: 0; 340 | margin-bottom: 1rem; 341 | font-weight: 400; 342 | position: relative; 343 | } 344 | 345 | .content h2 { 346 | font-size: 2em; 347 | line-height: 1.5em; 348 | margin: 3rem 0 1rem; 349 | font-weight: 400; 350 | border-top: 2px solid #dedede; 351 | padding-top: 1rem; 352 | } 353 | 354 | .content h3 { 355 | font-size: 1em; 356 | line-height: 1.5em; 357 | font-weight: 500; 358 | margin: .5rem 0; 359 | } 360 | 361 | .side-header h2 { 362 | font-weight: 500; 363 | font-size: 18.5px; 364 | line-height: 24px; 365 | margin-top: 7px; 366 | border-top: none; 367 | padding-top: 0; 368 | } 369 | 370 | .side-header h3 { 371 | font-size: 1em; 372 | margin-top: 0px; 373 | } 374 | 375 | .side-header p { 376 | color: #777; 377 | } 378 | 379 | @media screen and (min-width: 820px) { 380 | .faq { 381 | /*padding-right: 6em;*/ 382 | } 383 | 384 | .faq p, 385 | .faq ul, 386 | .faq ol, 387 | .faq blockquote, 388 | .faq pre, 389 | .faq table { 390 | margin-left: 8rem; 391 | } 392 | } 393 | 394 | .faq .faq-intro { 395 | margin-left: 0rem; 396 | } 397 | 398 | .faq blockquote { 399 | border-left: .25em solid #dedede; 400 | padding-left: 2rem; 401 | } 402 | 403 | .faq blockquote p { 404 | margin: 0; 405 | } 406 | 407 | .faq pre { 408 | margin-top: 2rem; 409 | margin-bottom: 2rem; 410 | } 411 | 412 | .faq code { 413 | display: inline-block; 414 | padding: .1em .2em 0; 415 | font-size: 0.95em; 416 | line-height: 1em; 417 | background-color: #f7f7f7; 418 | border-radius: 2px; 419 | font-weight: 400; 420 | font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; 421 | } 422 | 423 | .faq pre code { 424 | font-size: 1em; 425 | line-height: 1.5em; 426 | } 427 | 428 | .faq h3 > a { 429 | color: black; 430 | } 431 | 432 | .faq ol ul { 433 | padding-left: 0px; 434 | margin-top: 0.5em; 435 | margin-left: 1em; 436 | } 437 | 438 | #toc h2 { 439 | border: 0; 440 | font-size: 2rem; 441 | line-height: 1.5em; 442 | } 443 | 444 | #toc .contents { 445 | -webkit-columns: 3 150px; 446 | -moz-columns: 3 150px; 447 | columns: 3 150px; 448 | -webkit-perspective: 1; 449 | } 450 | 451 | #toc ol { 452 | margin: 0 0 0 2rem; 453 | padding: 0; 454 | } 455 | 456 | #users > h1 { 457 | text-align: center; 458 | padding-top: 10px; 459 | font-size: 2.5em; 460 | font-weight: 400; 461 | margin: 0px; 462 | padding-top: 50px; 463 | } 464 | 465 | #users > h2 { 466 | text-align: center; 467 | font-size: 1.5em; 468 | line-height: 1.5em; 469 | font-weight: 400; 470 | margin: 0px; 471 | padding-bottom: 60px; 472 | } 473 | 474 | #users > div > div { 475 | text-align: center; 476 | } 477 | 478 | #users .user-container { 479 | margin-left: auto; 480 | margin-right: auto; 481 | width: 200px; 482 | height: 100px; 483 | } 484 | 485 | #users .user-container a { 486 | line-height: 100px; 487 | } 488 | 489 | #users img { 490 | max-width: 200px; 491 | max-height: 100px; 492 | vertical-align: middle; 493 | } 494 | 495 | #users .details { 496 | margin-top: 2em; 497 | margin-bottom: 2em; 498 | } 499 | 500 | #users .fade-in { 501 | transition: opacity 0.5s linear; 502 | } 503 | 504 | #users .fade-out { 505 | transition: opacity 0.1s ease-in-out; 506 | } 507 | 508 | #users .fade-in * { 509 | z-index: 1; 510 | } 511 | 512 | #users .fade-out * { 513 | z-index: -1; 514 | } 515 | 516 | @media (min-width: 992px) { 517 | 518 | #users .fade-in { 519 | opacity: 1; 520 | } 521 | 522 | #users .fade-out { 523 | opacity: 0; 524 | } 525 | 526 | } 527 | 528 | #users .user-details-row-1 div { 529 | height: 2em; 530 | margin-top: 0em; 531 | } 532 | 533 | #users .user-details-row-2 div { 534 | height: 2em; 535 | margin-top: -2em; 536 | } 537 | 538 | #users .user-details-row-3 div { 539 | height: 2em; 540 | margin-top: -2em; 541 | } 542 | 543 | #users .details p { 544 | margin: 0px; 545 | } 546 | 547 | #users em { 548 | font-weight: bold; 549 | } 550 | 551 | #users #user-add-info { 552 | text-align: center; 553 | padding-top: 50px; 554 | } 555 | 556 | .form input { 557 | font-size: 3em; 558 | width: 100%; 559 | } 560 | 561 | #output { 562 | font-size: 2.4em; 563 | width: 100%; 564 | word-wrap: break-word; 565 | } 566 | #number-out { 567 | font-size: 2.4em; 568 | } 569 | 570 | aside { 571 | border: 1px solid black; 572 | border-width: 1px 1px 1px 5px; 573 | padding: 5px; 574 | margin: 5px 0; 575 | } 576 | aside p { 577 | margin: 0.7em; 578 | } 579 | 580 | .deprecation-notice { 581 | text-align: center; 582 | background-color: #fffbdd; 583 | color: #735c0f; 584 | } 585 | -------------------------------------------------------------------------------- /talks.md: -------------------------------------------------------------------------------- 1 | permalink: /talks 2 | title: Talks 3 | layout: site.liquid 4 | data: 5 | route: talks 6 | --- 7 | ## Talks & Workshops 8 | 9 | A collection of talks and workshops about Rust, WebAssembly and connected topics. 10 | You have a talk that's missing from this list? [Tell us about it!](https://github.com/badboy/hellorust/issues/new) 11 | 12 | --- 13 | 14 | ### WebAssembly for the Rest of Us 15 | 16 | **by Jan-Erik Rediger** at 17 | [JS Kongress Munich](https://2017.js-kongress.de/sessions/webassembly-rest-us/) & 18 | [Mozilla Dev Roadshow (Munich)](https://www.meetup.com/MuniCSS-finest/events/243276897/) 19 | 20 | When: 21 | 2017-11-14, 2017-11-15 22 | 23 | Material: 24 | 25 | * [Slides](https://fnordig.de/talks/2017/jskongress/) 26 | * [Video](https://www.youtube.com/watch?v=SGkZbxIGDNE) 27 | 28 | --- 29 | 30 | ### Interaction with real-world JavaScript from compiled Rust 31 | 32 | **by Ingvar Stepanyan** at 33 | [RustFest Kyiv 2017](http://2017.rustfest.eu/talks/#interaction-with-real-world-javascript-from-compiled-rust) 34 | 35 | Material: 36 | 37 | * [Video](https://www.youtube.com/watch?v=zxIbTfsOJZE) 38 | 39 | --- 40 | 41 | ### WebAssembly for the Rest of Us 42 | 43 | **by Jan-Erik Rediger** at 44 | [Codemotion Amsterdam](http://amsterdam2017.codemotionworld.com/) & 45 | [Mozilla Dev Roadshow (Düsseldorf)](https://beyondtellerrand.com/events/duesseldorf-2017/side-events/mozilla-roadshow) 46 | 47 | When: 48 | 2017-05-16, 2017-05-17 49 | 50 | Material: 51 | 52 | * [Slides](http://www.hellorust.com/codemotion-ams/slides/) 53 | 54 | --- 55 | 56 | ### Hire me for the JS, keep me for the Rust 57 | 58 | **by Jan-Erik Rediger** at [Rust Belt Rust 2016](http://www.rust-belt-rust.com/sessions/) 59 | 60 | When: 61 | 2016-10-27 62 | 63 | Material: 64 | * [Slides Talk](http://www.hellorust.com/emscripten/slides/rbr-talk) 65 | * [Slides Hands-on](http://www.hellorust.com/emscripten/slides/handson/) 66 | * [Demos](http://www.hellorust.com/emscripten/demos/) 67 | 68 | --- 69 | 70 | ### Compiling Rust to your Browser 71 | 72 | **by Jan-Erik Rediger** at [Rust Cologne/Bonn](https://www.meetup.com/Rust-Cologne-Bonn/events/233139845/) 73 | 74 | When: 75 | 2016-09-05 76 | 77 | Material: 78 | * [Slides](https://badboy.github.io/rust-to-the-browser/) 79 | * [Video](https://media.ccc.de/v/rustmcb.2016.09.compiling-rust-to-asmjs) 80 | --------------------------------------------------------------------------------