├── .gitignore ├── .vscode ├── settings.json └── tasks.json ├── README.adoc ├── asciidoctor-pdf.js ├── .gitignore ├── LICENSE ├── README.adoc ├── bin │ └── asciidoctorjs-pdf ├── lib │ ├── cli.js │ └── converter.js ├── package-lock.json └── package.json ├── lectures ├── 00-template │ ├── slides.adoc │ └── slides.pdf ├── 01-intro │ ├── UB.png │ ├── avg │ │ ├── .gitignore │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── MainJ.java │ │ ├── MainS.scala │ │ └── src │ │ │ └── main.rs │ ├── csc.svg │ ├── ferrous.svg │ ├── heap.svg │ ├── overflow │ │ └── main.rs │ ├── slides.adoc │ ├── slides.pdf │ ├── stack.svg │ ├── stack_heap_java.bob │ ├── stack_heap_java.svg │ ├── stack_heap_rust.bob │ ├── stack_heap_rust.svg │ ├── tuples │ │ ├── main.py │ │ └── main.rs │ ├── vec1.svg │ ├── vec2.svg │ └── vec3.svg ├── 02-borrowing │ ├── binary_search.rs │ ├── slides.adoc │ └── slides.pdf ├── 03-traits │ ├── slides.adoc │ └── slides.pdf ├── 04-crates │ ├── ffi │ │ ├── c-from-rust │ │ │ ├── foo.c │ │ │ └── main.rs │ │ └── rust-from-c │ │ │ ├── foo.rs │ │ │ └── main.c │ ├── slides.adoc │ └── slides.pdf ├── 05-functions │ ├── slides.adoc │ └── slides.pdf ├── 06-error-management │ ├── slides.adoc │ └── slides.pdf ├── 07-trait-objects │ ├── fat.bob │ ├── fat.svg │ ├── single_inheritance.bob │ ├── single_inheritance.svg │ ├── slides.adoc │ ├── slides.pdf │ ├── thin.bob │ ├── thin.svg │ ├── vtable.bob │ └── vtable.svg ├── 08-advanced-lifetimes │ ├── slides.adoc │ └── slides.pdf ├── 09-smart-pointers │ ├── code │ │ ├── const.rs │ │ ├── cow_lower.rs │ │ ├── dyn.rs │ │ ├── leak.rs │ │ ├── local_fn.rs │ │ ├── local_mod.rs │ │ ├── main.rs │ │ ├── make_mut.rs │ │ ├── match.rs │ │ ├── ns.rs │ │ ├── pi.rs │ │ ├── print_local.rs │ │ ├── rc.rs │ │ ├── ref_proj.rs │ │ ├── static_array.rs │ │ ├── static_counter.rs │ │ ├── static_elision.rs │ │ ├── static_lifetime.rs │ │ ├── struct_sugar.rs │ │ ├── tag.rs │ │ └── upper.rs │ ├── slides.adoc │ └── slides.pdf ├── 10-multithreading │ ├── code │ │ ├── Cargo.lock │ │ ├── Cargo.toml │ │ ├── crossbeam_scope.rs │ │ ├── move_vec.rs │ │ ├── non-racy.rs │ │ ├── racy.rs │ │ ├── racy2.rs │ │ ├── scope.rs │ │ ├── spawn.rs │ │ ├── spawn_builder.rs │ │ └── spawn_move.rs │ ├── mem.bob │ ├── mem.svg │ ├── model.bob │ ├── model.svg │ ├── scope │ ├── slides.adoc │ └── slides.pdf ├── 11-unsafe │ ├── code │ │ ├── dropck.rs │ │ ├── evil.rs │ │ ├── get_pair_mut.rs │ │ ├── get_pair_mut2.rs │ │ └── phantom.rs │ ├── slides.adoc │ ├── slides.pdf │ └── unsafe-ferris.png ├── 12-collections │ ├── slides.adoc │ └── slides.pdf ├── 13-macros │ ├── code │ │ └── nop.rs │ ├── fin.jpg │ ├── slides.adoc │ └── slides.pdf ├── asciidoctor.css ├── fin.jpg ├── rust-logo-blk.svg ├── slides.css └── template.js ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.html 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "emeraldwalk.runonsave": { 3 | "autoClearConsole": true, 4 | "commands": [ 5 | { 6 | "match": ".*\\.(js|css|rs)", 7 | "cmd": "npm run adoc -- --html ./lectures/*/*.adoc", 8 | }, 9 | { 10 | "match": ".*lectures.*\\.adoc", 11 | "cmd": "npm run adoc -- --html ${file}", 12 | }, 13 | { 14 | "match": ".*homework.*\\.adoc", 15 | "cmd": "asciidoctor-pdf ${file}", 16 | }, 17 | { 18 | "match": ".*\\.bob", 19 | "cmd": "svgbob ${file} -o ${fileDirname}/${fileBasenameNoExt}.svg" 20 | }, 21 | { 22 | "match": "main.rs", 23 | "cmd": "rustc main.rs && ./main ", 24 | } 25 | ] 26 | }, 27 | "liveServer.settings.AdvanceCustomBrowserCmdLine": "chromium-browser", 28 | "spellright.language": [ 29 | "en", 30 | "ru" 31 | ], 32 | "spellright.documentTypes": [ 33 | "markdown", 34 | "latex", 35 | "plaintext" 36 | ], 37 | "rust-analyzer.enableCargoWatchOnStartup": false 38 | } 39 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "adoc", 8 | "type": "shell", 9 | "command": "npm run adoc -- ${file}", 10 | "options": { 11 | "env": { 12 | "PUPPETEER_EXECUTABLE_PATH": "/run/current-system/sw/bin/chromium" 13 | } 14 | }, 15 | "presentation": { 16 | "echo": true, 17 | "reveal": "silent", 18 | "focus": false, 19 | "panel": "shared", 20 | "showReuseMessage": true, 21 | "clear": true 22 | }, 23 | "problemMatcher": [], 24 | "group": { 25 | "kind": "build", 26 | "isDefault": true 27 | } 28 | }, 29 | ] 30 | } 31 | -------------------------------------------------------------------------------- /README.adoc: -------------------------------------------------------------------------------- 1 | # Rust Course 2 | 3 | Slides for Rust course taught in https://compscicenter.ru/[Computer Science 4 | Center] in Winter-Spring 2019 (in Russian). 5 | 6 | Course Page:: 7 | https://compscicenter.ru/courses/rustprogramming/2019-spring/ 8 | 9 | Video:: 10 | https://www.youtube.com/playlist?list=PLlb7e2G7aSpTfhiECYNI2EZ1uAluUqE_e 11 | 12 | Slides:: 13 | In this repository :-) 14 | 15 | Homework:: 16 | Homework tasks are not distributed, but they followed 17 | http://www.realtimerendering.com/raytracing/Ray%20Tracing%20in%20a%20Weekend.pdf[Ray 18 | Tracing in a Weekend] pretty closely. 19 | 20 | ## Building 21 | 22 | [src,bash] 23 | ---- 24 | $ npm install 25 | $ npm run adoc -- ./lectures/01-intro/slides.adoc 26 | $ pdf-viewer ./lectures/01-intro/slides.pdf 27 | ---- 28 | 29 | Awesome https://github.com/Mogztter/asciidoctor-pdf.js/[Asciidoctor.js PDF] 30 | project is used to build the slides. See 31 | https://matklad.github.io/2019/05/19/consider-using-asciidoctor-for-your-next-presentation.html[this 32 | post] to learn more about this tool. 33 | 34 | ## License 35 | 36 | ++++ 37 | 38 | 39 | 40 | 41 | This work is licensed under a Creative Commons Attribution 4.0 International License. 42 | ++++ 43 | 44 | Asciidoctor.js PDF is vendored into this repository and is licensed under the MIT license. 45 | -------------------------------------------------------------------------------- /asciidoctor-pdf.js/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | *_temp.html 3 | -------------------------------------------------------------------------------- /asciidoctor-pdf.js/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Guillaume Grossetie 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /asciidoctor-pdf.js/README.adoc: -------------------------------------------------------------------------------- 1 | = Asciidoctor.js PDF 2 | 3 | ifdef::env-github[] 4 | image:https://travis-ci.org/Mogztter/asciidoctor-pdf.js.svg?branch=master[Travis build status, link=https://travis-ci.org/Mogztter/asciidoctor-pdf.js] 5 | endif::[] 6 | 7 | Convert AsciiDoc document to PDF using Asciidoctor.js and Puppeteer (Headless Chrome) 8 | 9 | == About 10 | 11 | Inspired by an awesome project called https://github.com/RelaxedJS/ReLaXed/blob/master/src/index.js[Relaxed.JS]. 12 | 13 | Asciidoctor.js PDF is using Headless Chrome to generate a beautiful PDF. 14 | It allows complex layouts to be defined with CSS and JavaScript, while writing the content in AsciiDoc. 15 | 16 | == Examples 17 | 18 | .Letter 19 | ./bin/asciidoctorjs-pdf ./examples/letter/letter.adoc --template-require ./examples/letter/template.js 20 | 21 | .Book 22 | ./bin/asciidoctorjs-pdf ./examples/book/book.adoc --template-require ./examples/book/template.js 23 | 24 | .Document 25 | ./bin/asciidoctorjs-pdf ./examples/document/document.adoc --template-require ./examples/document/templates/index.js 26 | 27 | .slides 28 | ./bin/asciidoctorjs-pdf ./examples/slides/slides.adoc --template-require ./examples/slides/template.js 29 | -------------------------------------------------------------------------------- /asciidoctor-pdf.js/bin/asciidoctorjs-pdf: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | 'use strict' 4 | 5 | process.title = 'asciidoctorjs-pdf' 6 | const cli = require('../lib/cli.js') 7 | 8 | async function main () { 9 | const argv = cli.argsParser().argv 10 | return cli.run(argv) 11 | } 12 | 13 | main() 14 | .then((result) => { 15 | if (result.exit) { 16 | process.exit(0) 17 | } 18 | }) 19 | .catch((error) => { 20 | console.log('error', error) 21 | process.exit(1) 22 | }) 23 | -------------------------------------------------------------------------------- /asciidoctor-pdf.js/lib/cli.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | // NOTE: we should extend asciidoctor-cli.js 4 | 5 | const yargs = require('yargs') 6 | const converter = require('./converter') 7 | const chokidar = require('chokidar') 8 | const path = require('path') 9 | const DOT_RELATIVE_RX = new RegExp(`^\\.{1,2}[/${path.sep.replace('/', '').replace('\\', '\\\\')}]`) 10 | 11 | 12 | function convertOptions (argv) { 13 | const backend = argv['backend'] 14 | const doctype = argv['doctype'] 15 | const safeMode = argv['safe-mode'] 16 | const noHeaderFooter = argv['no-header-footer'] 17 | const sectionNumbers = argv['section-numbers'] 18 | const baseDir = argv['base-dir'] 19 | const destinationDir = argv['destination-dir'] 20 | const quiet = argv['quiet'] 21 | const verbose = argv['verbose'] 22 | const timings = argv['timings'] 23 | const trace = argv['trace'] 24 | const requireLib = argv['require'] 25 | const templateRequireLib = argv['template-require'] 26 | if (verbose) { 27 | console.log('require ' + requireLib) 28 | console.log('template-require ' + templateRequireLib) 29 | console.log('backend ' + backend) 30 | console.log('doctype ' + doctype) 31 | console.log('header-footer ' + !noHeaderFooter) 32 | console.log('section-numbers ' + sectionNumbers) 33 | console.log('quiet ' + quiet) 34 | console.log('verbose ' + verbose) 35 | console.log('timings ' + timings) 36 | console.log('trace ' + trace) 37 | console.log('base-dir ' + baseDir) 38 | console.log('destination-dir ' + destinationDir) 39 | } 40 | if (requireLib) { 41 | require(requireLib) 42 | } 43 | const verboseMode = quiet ? 0 : verbose ? 2 : 1 44 | const attributes = [] 45 | if (noHeaderFooter) { 46 | attributes.push('showtitle') 47 | } 48 | if (sectionNumbers) { 49 | attributes.push('sectnums') 50 | } 51 | const cliAttributes = argv['attribute'] 52 | if (cliAttributes) { 53 | attributes.push(...cliAttributes) 54 | } 55 | if (verbose) { 56 | console.log('verbose-mode ' + verboseMode) 57 | console.log('attributes ' + attributes) 58 | } 59 | const options = { 60 | backend: backend, 61 | doctype: doctype, 62 | safe: safeMode, 63 | header_footer: !noHeaderFooter, 64 | verbose: verboseMode, 65 | timings: timings, 66 | trace: trace, 67 | noPdf: argv["html"] 68 | } 69 | if (baseDir != null) { 70 | options.base_dir = baseDir 71 | } 72 | if (destinationDir != null) { 73 | options.to_dir = destinationDir 74 | } 75 | options.to_file = false 76 | options.attributes = attributes 77 | if (templateRequireLib) { 78 | converter.registerTemplateConverter(requireLibrary(templateRequireLib)) 79 | } 80 | if (verbose) { 81 | console.log('options ' + JSON.stringify(options)) 82 | } 83 | return options 84 | } 85 | 86 | function requireLibrary (requirePath, cwd = process.cwd()) { 87 | if (requirePath.charAt() === '.' && DOT_RELATIVE_RX.test(requirePath)) { 88 | // NOTE require resolves a dot-relative path relative to current file; resolve relative to cwd instead 89 | requirePath = path.resolve(requirePath) 90 | } else if (!path.isAbsolute(requirePath)) { 91 | // NOTE appending node_modules prevents require from looking elsewhere before looking in these paths 92 | const paths = [cwd, path.dirname(__dirname)].map((start) => path.join(start, 'node_modules')) 93 | requirePath = require.resolve(requirePath, { paths }) 94 | } 95 | return require(requirePath) 96 | } 97 | 98 | function argsParser () { 99 | return yargs 100 | .detectLocale(false) 101 | .wrap(Math.min(120, yargs.terminalWidth())) 102 | .command('$0 [files...]', '', function (yargs) { 103 | return yargs 104 | .option('doctype', { 105 | alias: 'd', 106 | default: 'article', 107 | describe: 'document type to use when converting document', 108 | choices: ['article', 'book', 'manpage', 'inline'] 109 | }) 110 | .option('template-require', { 111 | describe: 'require the specified template script', 112 | type: 'string' 113 | }) 114 | .option('out-file', { 115 | alias: 'o', 116 | describe: 'output file (default: based on path of input file) use \'\' to output to STDOUT', 117 | type: 'string' 118 | }) 119 | .option('safe-mode', { 120 | alias: 'S', 121 | default: 'unsafe', 122 | describe: 'set safe mode level explicitly, disables potentially dangerous macros in source files, such as include::[]', 123 | choices: ['unsafe', 'safe', 'server', 'secure'] 124 | }) 125 | .option('no-header-footer', { 126 | alias: 's', 127 | default: false, 128 | describe: 'suppress output of header and footer', 129 | type: 'boolean' 130 | }) 131 | .option('html', { 132 | default: false, 133 | describe: 'suppress output of pdf', 134 | type: 'boolean' 135 | }) 136 | .option('section-numbers', { 137 | alias: 'n', 138 | default: false, 139 | describe: 'auto-number section titles in the HTML backend disabled by default', 140 | type: 'boolean' 141 | }) 142 | .option('base-dir', { 143 | // QUESTION: should we check that the directory exists ? coerce to a directory ? 144 | alias: 'B', 145 | describe: 'base directory containing the document and resources (default: directory of source file)', 146 | type: 'string' 147 | }) 148 | .option('destination-dir', { 149 | // QUESTION: should we check that the directory exists ? coerce to a directory ? 150 | alias: 'D', 151 | describe: 'destination output directory (default: directory of source file)', 152 | type: 'string' 153 | }) 154 | .option('quiet', { 155 | alias: 'q', 156 | default: false, 157 | describe: 'suppress warnings', 158 | type: 'boolean' 159 | }) 160 | .option('trace', { 161 | default: false, 162 | describe: 'include backtrace information on errors', 163 | type: 'boolean' 164 | }) 165 | .option('verbose', { 166 | alias: 'v', 167 | default: false, 168 | describe: 'enable verbose mode', 169 | type: 'boolean' 170 | }) 171 | .option('timings', { 172 | alias: 't', 173 | default: false, 174 | describe: 'enable timings mode', 175 | type: 'boolean' 176 | }) 177 | .option('attribute', { 178 | alias: 'a', 179 | array: true, 180 | describe: 'a document attribute to set in the form of key, key! or key=value pair', 181 | type: 'string' 182 | }) 183 | .option('require', { 184 | alias: 'r', 185 | array: true, 186 | describe: 'require the specified library before executing the processor, using the standard Node require', 187 | type: 'string' 188 | }) 189 | .option('watch', { 190 | alias: 'w', 191 | default: false, 192 | describe: 'enable watch mode', 193 | type: 'boolean' 194 | }) 195 | .option('version', { 196 | alias: 'V', 197 | default: false, 198 | describe: 'display the version and runtime environment (or -v if no other flags or arguments)', 199 | type: 'boolean' 200 | }) 201 | }) 202 | .version(false) 203 | .strict() 204 | .help() 205 | } 206 | 207 | async function convertFiles (files, argv, options, verbose) { 208 | for (let file of files) { 209 | if (verbose) { 210 | console.log(`converting file ${file}`) 211 | } 212 | await converter.convert(file, options, argv['timings']) 213 | } 214 | } 215 | 216 | async function run (argv) { 217 | const verbose = argv['verbose'] 218 | const version = argv['version'] 219 | const files = argv['files'] 220 | const watch = argv['watch'] 221 | const options = convertOptions(argv) 222 | if (version || (verbose && files && files.length === 0)) { 223 | console.log(`Asciidoctor ${converter.getCoreVersion()} [http://asciidoctor.org]`) 224 | const releaseName = process.release ? process.release.name : 'node' 225 | console.log(`Runtime Environment (${releaseName} ${process.version} on ${process.platform})`) 226 | return { exit: true } 227 | } else if (files && files.length > 0) { 228 | await convertFiles(files, argv, options, verbose) 229 | if (watch) { 230 | const watchFiles = files.map((file) => { 231 | const dirname = path.dirname(file) 232 | const allSubdirPath = path.join(dirname, '**') 233 | return [ 234 | path.join(allSubdirPath, '*.css'), 235 | path.join(allSubdirPath, '*.js'), 236 | path.join(allSubdirPath, '*.adoc'), 237 | file 238 | ] 239 | }) 240 | chokidar.watch(watchFiles, { ignored: /(^|[\/\\])\../ }).on('all', async (event, path) => { 241 | if (event === 'change') { 242 | console.log(' ' + event, path) 243 | try { 244 | await convertFiles(files, argv, options, verbose) 245 | } catch (e) { 246 | console.log('error', e); 247 | } 248 | } 249 | }); 250 | return { exit: false }; 251 | } else { 252 | return { exit: true }; 253 | } 254 | } else { 255 | yargs.showHelp() 256 | return { exit: true }; 257 | } 258 | } 259 | 260 | module.exports = { 261 | run: run, 262 | argsParser: argsParser 263 | } 264 | -------------------------------------------------------------------------------- /asciidoctor-pdf.js/lib/converter.js: -------------------------------------------------------------------------------- 1 | /* global Opal */ 2 | const util = require('util') 3 | const fs = require('fs') 4 | const path = require('path') 5 | const writeFile = util.promisify(fs.writeFile) 6 | const puppeteer = require('puppeteer') 7 | const asciidoctor = require('asciidoctor.js')() 8 | const writeFileAtomicSync = require('write-file-atomic').sync 9 | 10 | function registerTemplateConverter (templates) { 11 | class TemplateConverter { 12 | constructor () { 13 | this.baseConverter = asciidoctor.Html5Converter.$new() 14 | this.templates = templates; 15 | } 16 | 17 | convert (node, transform, opts) { 18 | const template = this.templates[transform || node.node_name] 19 | if (template) { 20 | return template(node) 21 | } 22 | return this.baseConverter.convert(node, transform, opts) 23 | } 24 | } 25 | asciidoctor.ConverterFactory.register(new TemplateConverter(), ['html5']) 26 | } 27 | 28 | async function convert(inputFile, options, timings) { 29 | let html; 30 | let doc; 31 | if (timings) { 32 | const timings = asciidoctor.Timings.$new() 33 | const instanceOptions = Object.assign({}, options, { timings: timings }) 34 | doc = asciidoctor.loadFile(inputFile, instanceOptions) 35 | html = doc.convert(instanceOptions) 36 | timings.$print_report(Opal.gvars.stderr, inputFile) 37 | } else { 38 | doc = asciidoctor.loadFile(inputFile, options) 39 | html = doc.convert(options) 40 | } 41 | const workingDir = path.dirname(inputFile) 42 | const inputFilenameWithoutExt = path.basename(inputFile, path.extname(inputFile)) 43 | const outputFile = path.join(workingDir, inputFilenameWithoutExt + '.pdf') 44 | let tempFile; 45 | if (path.isAbsolute(workingDir)) { 46 | tempFile = path.join(workingDir, inputFilenameWithoutExt + '_temp.html') 47 | } else { 48 | tempFile = path.normalize(path.join(process.cwd(), workingDir, inputFilenameWithoutExt + '_temp.html')) 49 | } 50 | writeFileAtomicSync(tempFile, html); 51 | if (options.noPdf) { 52 | return; 53 | } 54 | 55 | const puppeteerConfig = { 56 | headless: true, 57 | args: ['--no-sandbox'] 58 | } 59 | const browser = await puppeteer.launch(puppeteerConfig); 60 | try { 61 | const page = await browser.newPage() 62 | page 63 | .on('pageerror', err => console.log('Page error: ' + err.toString())) 64 | .on('error', err => console.log('Error: ' + err.toString())) 65 | await page.goto('file://' + tempFile, { waitUntil: 'networkidle2' }) 66 | const pdfOptions = { 67 | path: outputFile, 68 | printBackground: true, 69 | preferCSSPageSize: true, 70 | } 71 | let pdfWidth = doc.getAttributes()['pdf-width'] 72 | if (pdfWidth) { 73 | pdfOptions.width = pdfWidth 74 | } 75 | let pdfHeight = doc.getAttributes()['pdf-height'] 76 | if (pdfHeight) { 77 | pdfOptions.height = pdfHeight 78 | } 79 | let format = doc.getAttributes()['pdf-format'] 80 | if (format) { 81 | pdfOptions.format = format // Paper format. If set, takes priority over width or height options. Defaults to 'Letter'. 82 | } 83 | return await page.pdf(pdfOptions) 84 | } finally { 85 | try { 86 | await browser.close() 87 | } catch (err) { 88 | console.log('Unable to close the browser - Error: ' + err.toString()) 89 | } 90 | } 91 | } 92 | 93 | function getCoreVersion() { 94 | return asciidoctor.getCoreVersion() 95 | } 96 | 97 | module.exports = { 98 | convert: convert, 99 | getCoreVersion: getCoreVersion, 100 | registerTemplateConverter: registerTemplateConverter 101 | } 102 | -------------------------------------------------------------------------------- /asciidoctor-pdf.js/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "asciidoctor-pdf.js", 3 | "version": "1.0.0", 4 | "description": "A PDF converter for AsciiDoc based on Asciidoctor.js", 5 | "main": "bin/asciidoctorjs-pdf", 6 | "engines": { 7 | "node": ">=8.11", 8 | "npm": ">=5.0.0" 9 | }, 10 | "files": [ 11 | "bin", 12 | "lib" 13 | ], 14 | "scripts": { 15 | "test": "./bin/asciidoctorjs-pdf --version" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "git+https://github.com/Mogztter/asciidoctor-pdf.js.git" 20 | }, 21 | "keywords": [], 22 | "author": "", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/Mogztter/asciidoctor-pdf.js/issues" 26 | }, 27 | "homepage": "https://github.com/Mogztter/asciidoctor-pdf.js#readme", 28 | "dependencies": { 29 | "asciidoctor.js": "1.5.9", 30 | "chokidar": "2.0.4", 31 | "puppeteer": "1.11.0", 32 | "write-file-atomic": "^2.3.0", 33 | "yargs": "11.0.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lectures/00-template/slides.adoc: -------------------------------------------------------------------------------- 1 | = Rust 2019 2 | Алексей Кладов 3 | :icons: font 4 | :lecture: Лекция N: Тема Лекции 5 | :table-caption!: 6 | :example-caption!: 7 | 8 | [.title-slide] 9 | == Раздел 10 | -------------------------------------------------------------------------------- /lectures/00-template/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/00-template/slides.pdf -------------------------------------------------------------------------------- /lectures/01-intro/UB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/01-intro/UB.png -------------------------------------------------------------------------------- /lectures/01-intro/avg/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | *.class 3 | **/*.rs.bk 4 | -------------------------------------------------------------------------------- /lectures/01-intro/avg/Cargo.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "avg" 3 | version = "0.1.0" 4 | 5 | -------------------------------------------------------------------------------- /lectures/01-intro/avg/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "avg" 3 | version = "0.1.0" 4 | authors = ["Aleksey Kladov "] 5 | edition = "2018" 6 | 7 | -------------------------------------------------------------------------------- /lectures/01-intro/avg/MainJ.java: -------------------------------------------------------------------------------- 1 | import java.io.BufferedReader; 2 | import java.io.FileNotFoundException; 3 | import java.io.FileReader; 4 | import java.util.Scanner; 5 | import java.util.Arrays; 6 | 7 | public class MainJ { 8 | public static void main(String[] args) { 9 | int N = 100000000; 10 | int[] xs = new int[N]; 11 | int random = N; 12 | for (int i = 0; i < N; i++) { 13 | random ^= random << 13; 14 | random ^= random >> 17; 15 | random ^= random << 5; 16 | xs[i] = random; 17 | } 18 | for (int i = 0; i < 100; i++) { 19 | long start = System.currentTimeMillis(); 20 | double res = average(xs); 21 | long end = System.currentTimeMillis(); 22 | if (i % 10 == 0) { 23 | System.out.println(res + " " + (end - start)); 24 | } 25 | } 26 | } 27 | 28 | private static double average(int[] data) { 29 | int sum = 0; 30 | for (int i = 0; i < data.length; i++) { 31 | sum += data[i]; 32 | } 33 | return sum * 1.0d / data.length; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lectures/01-intro/avg/MainS.scala: -------------------------------------------------------------------------------- 1 | import java.io.{BufferedReader, FileReader, StreamTokenizer} 2 | import java.util 3 | 4 | import scala.annotation.tailrec 5 | import scala.collection.mutable.ArrayBuffer 6 | import scala.util.Random 7 | import scala.collection.JavaConversions._ 8 | 9 | object MainS { 10 | def main(args: Array[String]): Unit = { 11 | val N = 100000000 12 | val xs = new Array[Int](N) 13 | var random = N 14 | for (i <- 0 until N) { 15 | random ^= random << 13 16 | random ^= random >> 17 17 | random ^= random << 5 18 | xs(i) = random 19 | } 20 | for (i <- 0 until 100) { 21 | val start = System.currentTimeMillis() 22 | val res = average(xs) 23 | val end = System.currentTimeMillis() 24 | if (i % 10 == 0) { 25 | println(res + " " + (end - start)) 26 | } 27 | } 28 | 29 | } 30 | def average(x: Array[Int]): Double = { 31 | x.reduce(_ + _) * 1.0 / x.size 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /lectures/01-intro/avg/src/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | const N: usize = 100000000; 3 | let mut xs = vec![0; N]; 4 | let mut random = N as i32; 5 | for i in 0..N { 6 | random ^= random << 13; 7 | random ^= random >> 17; 8 | random ^= random << 5; 9 | xs[i] = random; 10 | } 11 | for i in 0..100 { 12 | let start = std::time::Instant::now(); 13 | let res = average2(&xs); 14 | let elapsed = start.elapsed(); 15 | if i % 10 == 0 { 16 | println!("{} {}", res, elapsed.subsec_millis()); 17 | } 18 | } 19 | } 20 | 21 | fn average(xs: &[i32]) -> f64 { 22 | let mut sum: i32 = 0; 23 | for i in 0..xs.len() { 24 | sum += xs[i]; 25 | } 26 | sum as f64 / xs.len() as f64 27 | } 28 | 29 | fn average2(xs: &[i32]) -> f64 { 30 | xs.iter().fold(0, |x, y| x + y) as f64 / xs.len() as f64 31 | } 32 | -------------------------------------------------------------------------------- /lectures/01-intro/csc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | csc 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /lectures/01-intro/ferrous.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 200x70_ferrous systems_logo 5 | Created with Sketch. 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /lectures/01-intro/heap.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /lectures/01-intro/overflow/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let x = i32::max_value(); 3 | // let y = x + 1; 4 | // println!("{}", y); 5 | let x = i32::max_value(); 6 | let y = x.wrapping_add(1); 7 | assert_eq!(y, i32::min_value()); 8 | let y = x.saturating_add(1); 9 | assert_eq!(y, i32::max_value()); 10 | let (y, overflowed) = x.overflowing_add(1); 11 | assert!(overflowed); 12 | assert_eq!(y, i32::min_value()) 13 | } 14 | -------------------------------------------------------------------------------- /lectures/01-intro/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/01-intro/slides.pdf -------------------------------------------------------------------------------- /lectures/01-intro/stack.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lectures/01-intro/stack_heap_java.bob: -------------------------------------------------------------------------------- 1 | 2 | 3 | +----+----+ 4 | stack: | xs | p *- - -> 5 | +-*--+----+ 6 | | 7 | v 8 | +----+----+----+ 9 | heap: |ptr |len |cap | 10 | +-*--+----+----+ 11 | | 12 | v 13 | +----+----+ 14 | | p1 | p1 | 15 | +-*--+-*--+ 16 | +------+ | 17 | v v 18 | +----+----+ +----+----+ 19 | - - -> | x1 | y1 | | x2 | y2 | 20 | +----+----+ +----+----+ 21 | -------------------------------------------------------------------------------- /lectures/01-intro/stack_heap_java.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | stack: 135 | 136 | 137 | 138 | 139 | heap: 140 | 141 | 142 | 143 | 144 | x1 145 | 146 | 147 | 148 | 149 | y1 150 | 151 | 152 | 153 | 154 | ptr 155 | 156 | 157 | 158 | 159 | xs 160 | 161 | 162 | 163 | 164 | p1 165 | 166 | 167 | 168 | 169 | len 170 | 171 | 172 | 173 | 174 | p 175 | 176 | 177 | 178 | 179 | p1 180 | 181 | 182 | 183 | 184 | x2 185 | 186 | 187 | 188 | 189 | cap 190 | 191 | 192 | 193 | 194 | y2 195 | 196 | 197 | 198 | -------------------------------------------------------------------------------- /lectures/01-intro/stack_heap_rust.bob: -------------------------------------------------------------------------------- 1 | +----+----+----+----+ 2 | stack: |ptr |len |cap | p | 3 | +-*--+----+----+-*--+ 4 | | / 5 | | ,--------` 6 | | / 7 | v v 8 | +----+----+----+----+ 9 | heap: | x1 | y1 | x2 | y2 | 10 | +----+----+----+----+ 11 | -------------------------------------------------------------------------------- /lectures/01-intro/stack_heap_rust.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | stack: 105 | 106 | 107 | 108 | 109 | heap: 110 | 111 | 112 | 113 | 114 | ptr 115 | 116 | 117 | 118 | 119 | x1 120 | 121 | 122 | 123 | 124 | len 125 | 126 | 127 | 128 | 129 | y1 130 | 131 | 132 | 133 | 134 | cap 135 | 136 | 137 | 138 | 139 | x2 140 | 141 | 142 | 143 | 144 | p 145 | 146 | 147 | 148 | 149 | y2 150 | 151 | 152 | 153 | -------------------------------------------------------------------------------- /lectures/01-intro/tuples/main.py: -------------------------------------------------------------------------------- 1 | t = (92,) 2 | print(id(t)) 3 | print(id(t[0])) 4 | -------------------------------------------------------------------------------- /lectures/01-intro/tuples/main.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let t = (92,); 3 | println!("{:?}", &t as *const (i32,)); 4 | println!("{:?}", &t.0 as *const i32); 5 | 6 | let x = 8; 7 | let y = (8, 264); 8 | let x_view: &[u8; 4] = unsafe { &*(&x as *const i32 as *const [u8; 4])}; 9 | println!("{:02X?}", x_view); 10 | let y_view: &[u8; 8] = unsafe { &*(&y as *const (i32, i32) as *const [u8; 8])}; 11 | println!("{:02X?}", y_view); 12 | } 13 | -------------------------------------------------------------------------------- /lectures/02-borrowing/binary_search.rs: -------------------------------------------------------------------------------- 1 | use std::cmp::Ordering; 2 | 3 | fn binary_search(xs: &[i32], x: i32) -> bool { 4 | if xs.is_empty() { 5 | return false; 6 | } 7 | let mid = xs.len() / 2; 8 | let subslice = match xs[mid].cmp(&x) { 9 | Ordering::Less => &xs[mid + 1..], 10 | Ordering::Equal => return true, 11 | Ordering::Greater => &xs[..mid], 12 | }; 13 | binary_search(subslice, x) 14 | } 15 | 16 | fn main() {} 17 | -------------------------------------------------------------------------------- /lectures/02-borrowing/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/02-borrowing/slides.pdf -------------------------------------------------------------------------------- /lectures/03-traits/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/03-traits/slides.pdf -------------------------------------------------------------------------------- /lectures/04-crates/ffi/c-from-rust/foo.c: -------------------------------------------------------------------------------- 1 | #include "stdint.h" 2 | 3 | int32_t add(int32_t x, int32_t y) { 4 | return x + y; 5 | } 6 | -------------------------------------------------------------------------------- /lectures/04-crates/ffi/c-from-rust/main.rs: -------------------------------------------------------------------------------- 1 | extern "C" { 2 | fn add(x: i32, y: i32) -> i32; 3 | } 4 | 5 | fn main() { 6 | let x = unsafe { add(62, 30) }; 7 | println!("{}", x); 8 | } -------------------------------------------------------------------------------- /lectures/04-crates/ffi/rust-from-c/foo.rs: -------------------------------------------------------------------------------- 1 | #[no_mangle] 2 | pub extern "C" fn add(x: i32, y: i32) -> i32 { 3 | x + y 4 | } -------------------------------------------------------------------------------- /lectures/04-crates/ffi/rust-from-c/main.c: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "stdint.h" 3 | 4 | int32_t add(int32_t x, int32_t y); 5 | 6 | int main(void) { 7 | int32_t x = add(62, 30); 8 | printf("%d\n", (int)x); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /lectures/04-crates/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/04-crates/slides.pdf -------------------------------------------------------------------------------- /lectures/05-functions/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/05-functions/slides.pdf -------------------------------------------------------------------------------- /lectures/06-error-management/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/06-error-management/slides.pdf -------------------------------------------------------------------------------- /lectures/07-trait-objects/fat.bob: -------------------------------------------------------------------------------- 1 | +------------+ +------------+ 2 | | Cat* |-------->| Cat | 3 | +------------+ | Data | 4 | | AnimalVT* |--, +------------+ 5 | +------------+ \ 6 | \ +---------------+ 7 | `-->|"cat_say(Cat*)"| 8 | +---------------+ 9 | -------------------------------------------------------------------------------- /lectures/07-trait-objects/fat.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | Cat* 98 | 99 | 100 | 101 | 102 | AnimalVT* 103 | 104 | 105 | 106 | 107 | cat_say(Cat*) 108 | 109 | 110 | 111 | 112 | Cat 113 | 114 | 115 | 116 | 117 | Data 118 | 119 | 120 | -------------------------------------------------------------------------------- /lectures/07-trait-objects/single_inheritance.bob: -------------------------------------------------------------------------------- 1 | +--------+ +------------+ 2 | | VT* |----->| "base_foo"| 3 | +--------+ +------------+ 4 | | Base | | "base_bar"| 5 | | fields | +------------+ 6 | +--------+ | derived_baz| 7 | | Derived| +------------+ 8 | | fields| 9 | +--------+ 10 | -------------------------------------------------------------------------------- /lectures/07-trait-objects/single_inheritance.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | VT* 90 | 91 | 92 | 93 | 94 | Base 95 | 96 | 97 | 98 | 99 | fields 100 | 101 | 102 | 103 | 104 | Derived 105 | 106 | 107 | 108 | 109 | fields 110 | 111 | 112 | 113 | 114 | derived_baz 115 | 116 | 117 | 118 | 119 | base_foo 120 | 121 | 122 | 123 | 124 | base_bar 125 | 126 | 127 | -------------------------------------------------------------------------------- /lectures/07-trait-objects/slides.adoc: -------------------------------------------------------------------------------- 1 | = Rust 2019 2 | Алексей Кладов 3 | :icons: font 4 | :lecture: Лекция 7: Объекты, строки 5 | :table-caption!: 6 | :example-caption!: 7 | 8 | [.title-slide] 9 | == impl Trait 10 | 11 | == Как вернуть итератор? 12 | 13 | [source,rust] 14 | ---- 15 | fn random(n: usize) -> Vec { 16 | let mut r = 92; 17 | std::iter::repeat_with(move || { 18 | r ^= r << 13; 19 | r ^= r >> 17; 20 | r ^= r << 5; 21 | r 22 | }).take(n).collect() 23 | } 24 | ---- 25 | 26 | * не гибко -- аллоцируем `Vec` 27 | 28 | == Как вернуть итератор? 29 | 30 | [source,rust] 31 | ---- 32 | fn random>(n: usize) -> T { 33 | let mut r = 92; 34 | std::iter::repeat_with(move || { 35 | r ^= r << 13; 36 | r ^= r >> 17; 37 | r ^= r << 5; 38 | r 39 | }).take(n).collect() 40 | } 41 | ---- 42 | 43 | * лучше, но всё равно не удобно, не хочется думать про `n` 44 | 45 | == Как вернуть итератор? 46 | 47 | [source,rust] 48 | ---- 49 | fn random() -> ??? { 50 | let mut r = 92; 51 | std::iter::repeat_with(move || { 52 | r ^= r << 13; 53 | r ^= r >> 17; 54 | r ^= r << 5; 55 | r 56 | }) 57 | } 58 | ---- 59 | 60 | * написать конкретный тип не можем из-за лямбды 61 | * написать `+-> Iterator+` не получится -- + 62 | `Iterator` это не тип 63 | 64 | == Попытка 1 65 | 66 | [source,rust] 67 | ---- 68 | fn random>() -> T { 69 | let mut r = 92; 70 | std::iter::repeat_with(move || { 71 | r ^= r << 13; 72 | r ^= r >> 17; 73 | r ^= r << 5; 74 | r 75 | }) 76 | } 77 | ---- 78 | 79 | == Попытка 1 80 | 81 | * не работает: вызывающий код выбирает тип `T`: 82 | + 83 | [source,rust] 84 | ---- 85 | struct MyIter; 86 | impl Iterator for MyIter { type Item = u32; ... } 87 | 88 | fn main() { 89 | let _ = random::(); 90 | } 91 | ---- 92 | 93 | * "я могу вернуть **любой** `T` для котого верно + 94 | ``T: Iterator``" 95 | 96 | * нужно "**существует** какой-то ``T: Iterator``" 97 | 98 | == impl Trait 99 | 100 | [source,rust] 101 | ---- 102 | fn random() -> impl Iterator { 103 | let mut r = 92; 104 | std::iter::repeat_with(move || { 105 | r ^= r << 13; 106 | r ^= r >> 17; 107 | r ^= r << 5; 108 | r 109 | }) 110 | } 111 | ---- 112 | 113 | * [.language-rust]`+-> impl Trait+` не тип, синтаксис для ограниченного вывода типов (`+-> auto+` нет) 114 | * генератор машинного кода знает конкретный тип, но вывод типов знает только про `: Trait` 115 | * нельзя использовать в качестве типа поля 116 | 117 | == impl Trait 118 | 119 | [.language-rust]`impl Trait` можно использовать для аргументов, в качестве сахара для типовых параметров 120 | 121 | [source,rust] 122 | ---- 123 | fn process(items: impl Iterator) { 124 | 125 | } 126 | 127 | fn process>(items: I) { 128 | 129 | } 130 | ---- 131 | 132 | * аргумент: *forall* 133 | * возвращаемое значение: *exists* 134 | 135 | [.title-slide] 136 | == dyn Trait 137 | 138 | == ! 139 | 140 | [source,rust] 141 | ---- 142 | trait Say { 143 | fn say(&self); 144 | } 145 | 146 | impl Say for Cat { ... } 147 | impl Say for Dog { ... } 148 | 149 | fn main() { 150 | let mut animals = Vec::new(); 151 | animals.push(Cat::new("Garfield")); 152 | animals.push(Dog::new("The Hound of the Baskervilles")); 153 | for animal in animals.iter() { 154 | animal.say(); 155 | } 156 | } 157 | ---- 158 | 159 | * нельзя сложить `Cat` и `Dog` в вектор -- разный тип и размер 160 | * частично решили проблему в случае с функциями 161 | 162 | 163 | == Таблица Виртуальных Функций 164 | 165 | В Java работает, потому что и `Cat`, и `Dog` -- указатели: 166 | 167 | image::vtable.svg[width=100%] 168 | 169 | 170 | == Таблица Виртуальных Функций 171 | 172 | Объяснение на пальцах: 173 | 174 | Таблица вируальных функций -- структура из указателей на функции. Функции 175 | наследников записываются в конец. 176 | 177 | image::single_inheritance.svg[width=700] 178 | 179 | [.centered] 180 | == Таблица Виртуальных Функций 181 | 182 | [NOTE.question] 183 | Как выглядит таблица виртуальных функций для класса с несколькими интерфейсами? 184 | 185 | https://lukasatkinson.de/2018/interface-dispatch/ 186 | https://wiki.openjdk.java.net/display/HotSpot/VirtualCalls 187 | https://wiki.openjdk.java.net/display/HotSpot/InterfaceCalls 188 | 189 | 190 | == Интерфейсы в C++ 191 | 192 | Внутри каждого объекта -- несколько `vtable*` (по одной на каждый интерфейс). 193 | 194 | Каст `Derived*` к `Base*` это coercion, значение указателя меняется. 195 | 196 | Следствие: + 197 | Нельзя привести `vector` к `vector` 198 | 199 | == Интерфейсы в Java 200 | 201 | В Java, `List` *можно* привести к `List`. 202 | 203 | Вместо VTable -- указатель на класс. В классе -- список VTableов для каждого 204 | интерфейса. Для вызова метода интерфейса надо честно найти нужный интерфейс в 205 | списке. 206 | 207 | NOTE: JIT оптимистически всё инлайнит 208 | 209 | == thin pointer 210 | 211 | VTable можно сложить в объект (C++, C#, Java): 212 | 213 | image::thin.svg[width=100%] 214 | 215 | == fat pointer 216 | 217 | Но можно положить и рядом с указателем (Rust, Go): 218 | 219 | image::fat.svg[width=100%] 220 | 221 | == ! 222 | 223 | [.language-rust]`dyn Trait`, trait object:: 224 | Толстый указатель: пара из указателя на данные и указателя на таблицу виртуальных функций 225 | 226 | [source,rust] 227 | ---- 228 | trait Say { 229 | fn say(&self); 230 | } 231 | 232 | fn main() { 233 | let cat: Cat = Cat::new("Garfield"); 234 | let cat: &dyn Say = &cat; 235 | let dog: Dog = Dog::new("The Hound of the Baskervilles"); 236 | let dog: &dyn Say = &dog; 237 | let animals = vec![cat, dog]; 238 | for animal in animals.iter() { 239 | animal.say(); 240 | } 241 | } 242 | ---- 243 | 244 | == ! 245 | 246 | [source,rust] 247 | ---- 248 | trait Say { 249 | fn say(&self); 250 | } 251 | 252 | impl Say for i32 { 253 | fn say(&self) { 254 | println!("hm... int-int?") 255 | } 256 | } 257 | 258 | fn main() { 259 | let i: i32 = 92; 260 | let i: &dyn Say = &i; 261 | let animals = vec![i]; 262 | for animal in animals.iter() { 263 | animal.say(); 264 | } 265 | } 266 | ---- 267 | 268 | [.centered] 269 | == ! 270 | 271 | .Замыкания часто используются с [.language-rust]`dyn Trait`: 272 | [source,rust] 273 | ---- 274 | type Callback = Box u32>; 275 | 276 | fn adder(x: u32) -> Callback { 277 | Box::new(move |y| x + y) 278 | } 279 | 280 | fn multiplier(x: u32) -> Callback { 281 | Box::new(move |y| x * y) 282 | } 283 | ---- 284 | 285 | [.language-rust]`+Box U>+` -- аналог `std::function` 286 | 287 | 288 | == dyn Trait 289 | 290 | Трейты механизм и статического, и динамического полиморфизма 291 | 292 | Dynamic dispatch работает для чужих типов 293 | 294 | .[.language-rust]`dyn Trait` можно использовать с любым указателем: 295 | [source] 296 | ---- 297 | size_of::<&dyn Say>() 298 | == size_of::>() 299 | == size_of::<*const dyn Say>() 300 | == size_of::() * 2 301 | ---- 302 | 303 | == Dynamically Sized Types 304 | 305 | `[T]` и [.language-rust]`dyn Trait` это примеры DST: размер объекта не 306 | определяется статически. Обращение к DST -- через толстый указатель. 307 | 308 | Для не DST типов верно `: Sized`. Типовые параметры по умолчанию `Sized` 309 | 310 | .`std::mem` 311 | [source,rust,subs=+quotes] 312 | ---- 313 | fn size_of() -> usize // неявный where T: Sized 314 | fn size_of_val(val: &T) -> usize // убрали ^ 315 | ---- 316 | 317 | == Dynamically Sized Types 318 | 319 | [source,rust] 320 | ---- 321 | pub trait Index { 322 | type Output: ?Sized; 323 | 324 | fn index(&self, index: Idx) -> &Self::Output; 325 | } 326 | 327 | impl Index> for [T] { 328 | type Output = [T]; // unsized! 329 | fn index(&self, index: I) -> &[T] 330 | } 331 | ---- 332 | 333 | CAUTION: на слайдах предыдущих лекций я не писал `: ?Sized` 334 | 335 | == Object Safety 336 | 337 | Не из каждого трейта можно приготовить trait object 338 | 339 | .Нельзя возвращать/принимать `Self` по значению: 340 | [source,rust] 341 | ---- 342 | trait Clone { 343 | fn clone(&self) -> Self; 344 | } 345 | ---- 346 | 347 | .Нельзя использовать ассоциированные функции: 348 | [source,rust] 349 | ---- 350 | trait Default { 351 | fn default() -> Self; 352 | } 353 | ---- 354 | 355 | .Нельзя использовать параметризованные методы: 356 | [source,rust] 357 | ---- 358 | trait Hash { 359 | fn hash(&self, state: &mut H) 360 | } 361 | ---- 362 | 363 | == Когда Self: Sized 364 | 365 | [source,rust] 366 | ---- 367 | trait ObjectSafe { 368 | fn ok(&self); 369 | 370 | fn generic(&self, t: T) 371 | where Self: Sized; 372 | 373 | fn bad_self() -> Self 374 | where Self: Sized; 375 | } 376 | 377 | fn foo(obj: &dyn ObjectSafe) { 378 | obj.ok(); 379 | // the `generic` method cannot be invoked on a trait object 380 | // obj.generic::(92) 381 | } 382 | ---- 383 | 384 | Только не `Self: Sized` методы определяют object safety 385 | 386 | == Iterator 387 | 388 | [source,rust] 389 | ---- 390 | trait Iterator { 391 | type Item; 392 | fn next(&mut self) -> Option; // object safe 393 | 394 | fn map(self, f: F) -> Map // object safe! 395 | where Self: Sized, F: FnMut(Self::Item) -> B, 396 | { Map { iter: self, f } } 397 | 398 | ... 399 | } 400 | ---- 401 | 402 | 403 | Из итератора можно сделать [.language-rust]`Box>`, аналог `Iterator` из Java. Цена: аллокация в куче и dynamic dispatch на внешнем слое. 404 | 405 | == Iterator 406 | 407 | [source,rust] 408 | ---- 409 | fn with_any_iterator(it: &mut dyn Iterator) { 410 | let first = it.next(); // ok, next is object safe 411 | let it = it.filter(|it| it > 100) // ??? 412 | } 413 | ---- 414 | 415 | На [.language-rust]`dyn Iterator` можно позвать `.next`, но не понятно, что 416 | делать с комбинаторами: 417 | 418 | [source,rust] 419 | ---- 420 | fn filter_map(self, f: F) -> FilterMap 421 | where Self: Sized, F: FnMut(Self::Item) -> Option, 422 | { FilterMap { iter: self, f } } 423 | ---- 424 | 425 | Наш Self это [.language-rust]`&mut dyn Iterator` 426 | 427 | == Iterator 428 | 429 | [source,rust] 430 | ---- 431 | impl Iterator for &mut I { 432 | type Item = I::Item; 433 | fn next(&mut self) -> Option { ( 434 | **self).next() 435 | } 436 | fn size_hint(&self) -> (usize, Option) { 437 | (**self).size_hint() 438 | } 439 | fn nth(&mut self, n: usize) -> Option { 440 | (**self).nth(n) 441 | } 442 | } 443 | ---- 444 | 445 | [.language-rust]`dyn Iterator` это `: Iterator` => + 446 | [.language-rust]`&mut dyn Iterator` -- тоже `: Iterator` 447 | 448 | == Философия 449 | 450 | Dynamic dispatch -- мощный инструмент, вызов не известного кода 451 | 452 | Отлично подходит для плагинов, но затрудняет понимание кода 453 | 454 | Нарушает inline, требует косвенности => не супер быстрый 455 | 456 | В целом, [.language-rust]`dyn Trait` используется редко 457 | 458 | [.title-slide] 459 | == Строки 460 | 461 | == std::fmt::Debug и std::fmt::Display 462 | 463 | Трейты `Debug` и `Display` используются для превращения объектов в строки. 464 | 465 | [source,rust] 466 | ---- 467 | let text = "hello\nworld "; 468 | println!("{}", text); // Display 469 | println!("{:?}", text); // Debug 470 | ---- 471 | 472 | [source, sh] 473 | ---- 474 | $ ./main 475 | hello 476 | world 477 | "hello\nworld " 478 | ---- 479 | 480 | `Display` для пользователя = 481 | `Debug` для программиста 482 | 483 | == ! 484 | 485 | [source,rust] 486 | ---- 487 | #[derive(Debug)] // Debug можно реализовать автоматически 488 | struct Foo { 489 | x: i32 490 | } 491 | 492 | fn main() { 493 | let xs = vec![Foo { x: 1 }, Foo { x: 2 }], 494 | eprintln!("{:?}", xs); 495 | eprintln!("{:#?}", xs); // # включает альтернативный формат 496 | } 497 | ---- 498 | 499 | [source] 500 | ---- 501 | $ ./main 502 | [Foo { x: 1 }, Foo { x: 2 }] 503 | [ 504 | Foo { 505 | x: 1 506 | }, 507 | Foo { 508 | x: 2 509 | } 510 | ] 511 | ---- 512 | 513 | == Дизайн 514 | 515 | [source,rust] 516 | ---- 517 | trait Display { 518 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result; 519 | } 520 | ---- 521 | 522 | .Возвращать `String` плохо: 523 | * если выводим сразу в файл, то ненужная аллокация 524 | * можно выводить в буфер на стеке ограниченного размера 525 | * `N` отдельных аллокаций для подобъектов 526 | 527 | == Дизайн 528 | 529 | .Formatter абстрагирует назначение: 530 | [source,rust] 531 | ---- 532 | pub struct Formatter<'a> { 533 | flags: u32, 534 | fill: char, 535 | align: rt::v1::Alignment, 536 | width: Option, 537 | precision: Option, 538 | 539 | buf: &'a mut (dyn Write+'a), // trait object / NVI 540 | curarg: slice::Iter<'a, ArgumentV1<'a>>, 541 | args: &'a [ArgumentV1<'a>], 542 | } 543 | 544 | impl<'a> Formatter<'a> { 545 | fn write_str(&mut self, data: &str) -> fmt::Result; 546 | ... 547 | } 548 | ---- 549 | 550 | == ToString 551 | 552 | [source,rust] 553 | ---- 554 | pub trait ToString { <1> 555 | fn to_string(&self) -> String; 556 | } 557 | 558 | impl ToString for T { 559 | fn to_string(&self) -> String { 560 | use core::fmt::Write; 561 | let mut buf = String::new(); 562 | buf.write_fmt(format_args!("{}", self)) <2> 563 | .expect("Display returned an error unexpectedly"); 564 | buf.shrink_to_fit(); 565 | buf 566 | } 567 | } 568 | ---- 569 | 570 | <1> `ToString` -- чтобы можно было позвать `.to_string` 571 | <2> `format_args`: компилирует шаблон строки 572 | 573 | == String 574 | 575 | .Строки в Rust устроены просто: 576 | [source,rust] 577 | ---- 578 | pub struct String { 579 | vec: Vec, 580 | } 581 | ---- 582 | 583 | Вектор байт + **инвариант**, что в байтах валидный UTF-8 584 | 585 | == UTF-8 586 | 587 | Variable length encoding, совместима с ASCII, 1 байт для latin-1 588 | 589 | [source] 590 | ---- 591 | a : 0x61 592 | ы : 0xd1 0x8b 593 | 😀 : 0xf0 0x9f 0x98 0x80 594 | ---- 595 | 596 | UTF-8 -- кодировка по умолчанию в современных системах 597 | 598 | WARNING: нет random access доступа к символу (в целом не понятно, что такое символ) 599 | 600 | == UCS-2 / UTF-16 601 | 602 | В 1991 году думали, что 65536 символов хватит всем: UCS-2 это fixed width (16 603 | бит) кодировка с random access. 604 | 605 | Много систем изначально использовали UCS-2 (Java, Windows, JavaScript). 606 | 607 | В 1996 UCS-2 расширили до `UTF-16`: пара суррогатных символов кодирует один code point вне BMP. 608 | 609 | `UTF-16` -- тоже variable length, не random access. 610 | 611 | Многие языки используют UTF-16 с API UCS-2: можно получить невалидные данные в стоке. 612 | 613 | 614 | == API 615 | 616 | [source,rust] 617 | ---- 618 | impl String { 619 | fn new() -> String; 620 | fn with_capacity(capacity: usize) -> String; <1> 621 | fn from_utf8(vec: Vec) -> Result; <2> 622 | fn from_utf16(v: &[u16]) -> Result; <3> 623 | fn into_bytes(self) -> Vec; 624 | } 625 | 626 | impl FromUtf8Error { 627 | fn into_bytes(self) -> Vec; <4> 628 | } 629 | ---- 630 | 631 | <1> строки изменяемые (если у вас есть [.language-rust]`&mut`) 632 | <2> конверсия из UTF-8 не копирует 633 | <3> конверсия из UTF-6 копирует 634 | <4> в случае ошибки можно получить свой вектор назад 635 | 636 | == str 637 | 638 | `str` -- DST, `[u8]` + utf-8 инвариант. + 639 | `&str` -- указатель на байты + размер, неизменяемая строка 640 | 641 | [source,rust] 642 | ---- 643 | impl Deref for String { 644 | type Target = str; 645 | ... 646 | } 647 | 648 | impl Index> for String { 649 | type Output = str; 650 | } 651 | ---- 652 | 653 | `&str` можно получить из `String` через слайс: `&s[1..10]` 654 | 655 | Индексы для стайса -- позиции в байтах + 656 | Если позиция приходится на середину символа -- паника 657 | 658 | == Строки Без Копирования 659 | 660 | [source,rust] 661 | ---- 662 | impl str { 663 | fn split_whitespace<'a>(&'a self) -> SplitWhitespace<'a>; 664 | } 665 | 666 | impl<'a> Iterator for SplitWhitespace<'a> { 667 | type Item = &'a str; 668 | ... 669 | } 670 | ---- 671 | 672 | Многие методы на `&String` `&str` возвращают `&str` -- zero copy 673 | 674 | В отличие от старой Java и JavaScript нельзя получить memory leak: lifetime 675 | `&str` привязан к `String` 676 | 677 | == char 678 | 679 | |=== 680 | char 681 | |=== 682 | 683 | Примитивный тип для хранения Unicode Codepoint, 32 бита 684 | 685 | Используется редко: единица текста это grapheme cluster 686 | 687 | .API знает про Unicode: 688 | [source,rust] 689 | ---- 690 | impl char { 691 | to_lowercase(self) -> ToLowercase; // итератор! 692 | } 693 | 694 | impl Iterator for ToLowercase { 695 | type Item = char; 696 | } 697 | ---- 698 | 699 | == API 700 | 701 | [source,rust] 702 | ---- 703 | impl str { 704 | fn chars(&self) -> Char; // итератор charов 705 | 706 | fn to_ascii_lowercase(&self) -> String; 707 | fn make_ascii_uppercase(&mut self); 708 | 709 | // нельзя обойтись без аллокации 710 | fn to_lowercase(&self) -> String; 711 | 712 | // индекс в байтах, можно использовать в `[]` 713 | pub fn find<'a, P>(&'a self, pat: P) -> Option 714 | where P: Pattern<'a>; 715 | fn is_char_boundary(&self, index: usize) -> bool; 716 | 717 | fn as_bytes(&self) -> &[u8]; 718 | } 719 | 720 | // std::str 721 | pub fn from_utf8(v: &[u8]) -> Result<&str, Utf8Error>; 722 | ---- 723 | 724 | == Цена Абстракции 725 | 726 | В Rust есть два представления для последовательностей: 727 | 728 | . слайсы: `[T]`, `str` 729 | . итераторы: `Iterator` 730 | 731 | В теории, должно хватать только итераторов 732 | 733 | На практике, знание о том, что объекты лежат в памяти последовательно, позволяет 734 | значительно ускорить многие низкоуровневые операции (`memcpy`, `memchr`, etc). 735 | 736 | == CString / CStr 737 | 738 | Многие FFI строки используют `char *` -- байты, терминированные `0` 739 | 740 | `String` и `str` не совместимы с `char *`: нет нулевого байта в конце, нет 741 | гарантии про utf-8 742 | 743 | `CString` -- обёртка над `char *` для FFI 744 | 745 | WARNING: чтобы превратить `String` в `CString` нужна аллокация 746 | 747 | == OsString / OsStr 748 | 749 | * строки в Unix -- последовательность ненулевых байтов 750 | * строки в Windows -- "UTF-16" с непарными суррогатами 751 | 752 | `OsString` может представить любую строку ОС. Внутреннее представление -- WTF-8 753 | (надмножество Unicode для представления невалидный строк) 754 | 755 | `String` это `OsString`, `str` это `OsStr` 756 | 757 | == PathBuf / Path 758 | 759 | `PathBuf` обёртка над `OsString` для работы с путями 760 | 761 | Интерпретирует `/`, `\` как сепараторы + куча полезных методов 762 | 763 | NOTE: На windows `/` работает в качестве сепаратора 764 | 765 | == Итоги 766 | 767 | Строк много. Любая строка -- вектор байт, отличие в инвариантах 768 | 769 | |=== 770 | |owned|String|CString|OsString|PathBuf 771 | |borrowed|str|CStr|OsStr|Path 772 | |=== 773 | 774 | `Os` и `C` варианты -- редкие 775 | 776 | UTF-8 + байтовые индексы -- эффективное низкоуровневое представление 777 | -------------------------------------------------------------------------------- /lectures/07-trait-objects/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/07-trait-objects/slides.pdf -------------------------------------------------------------------------------- /lectures/07-trait-objects/thin.bob: -------------------------------------------------------------------------------- 1 | +---------+ +------------+ +---------------+ 2 | | Animal* |---> | AnimalVT* |--->|"cat_say(Cat*)"| 3 | +---------+ +------------+ +---------------+ 4 | | Cat | 5 | | Data | 6 | +------------+ 7 | -------------------------------------------------------------------------------- /lectures/07-trait-objects/thin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | Animal* 90 | 91 | 92 | 93 | 94 | AnimalVT* 95 | 96 | 97 | 98 | 99 | Cat 100 | 101 | 102 | 103 | 104 | Data 105 | 106 | 107 | 108 | 109 | cat_say(Cat*) 110 | 111 | 112 | -------------------------------------------------------------------------------- /lectures/07-trait-objects/vtable.bob: -------------------------------------------------------------------------------- 1 | +---------+ +------------+ +---------------+ 2 | | Animal* |---> | AnimalVT* |--->|"cat_say(Cat*)"| 3 | +---------+ +------------+ +---------------+ 4 | | Animal* |-, | Cat | 5 | +---------+ | | Data | 6 | | +------------+ 7 | | 8 | | +------------+ +---------------+ 9 | `-> | AnimalVT* |--->|"dog_say(Dog*)"| 10 | +------------+ +---------------+ 11 | | Dog | 12 | | Data | 13 | | | 14 | +------------+ 15 | -------------------------------------------------------------------------------- /lectures/07-trait-objects/vtable.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | Animal* 122 | 123 | 124 | 125 | 126 | Animal* 127 | 128 | 129 | 130 | 131 | AnimalVT* 132 | 133 | 134 | 135 | 136 | AnimalVT* 137 | 138 | 139 | 140 | 141 | Cat 142 | 143 | 144 | 145 | 146 | Data 147 | 148 | 149 | 150 | 151 | Dog 152 | 153 | 154 | 155 | 156 | Data 157 | 158 | 159 | 160 | 161 | cat_say(Cat*) 162 | 163 | 164 | 165 | 166 | dog_say(Dog*) 167 | 168 | 169 | -------------------------------------------------------------------------------- /lectures/08-advanced-lifetimes/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/08-advanced-lifetimes/slides.pdf -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/const.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug)] 2 | struct Color { 3 | r: u8, g: u8, b: u8 4 | } 5 | 6 | const BLACK: Color = Color { r: !0, g: !0, b: !0 }; 7 | 8 | fn main() { 9 | println!("men in {:?}", BLACK) 10 | } 11 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/cow_lower.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::Cow; 2 | 3 | fn to_lowercase<'a>(s: &'a str) -> Cow<'a, str> { 4 | if s.chars().all(char::is_lowercase) { 5 | Cow::Borrowed(s) 6 | } else { 7 | Cow::Owned(s.to_lowercase()) 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/dyn.rs: -------------------------------------------------------------------------------- 1 | type Callback<'a> = Box; 2 | 3 | fn main() { 4 | let f: Callback<'static> = 5 | Box::new(|| println!("hello")); 6 | 7 | let x = 92; 8 | let g: Callback<'_> = 9 | Box::new(|| println!("x = {}", x)); 10 | } 11 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/leak.rs: -------------------------------------------------------------------------------- 1 | impl Box { 2 | pub fn into_raw(b: Box) -> *mut T { ... } 3 | pub unsafe fn from_raw(raw: *mut T) -> Self { ... } 4 | 5 | pub fn leak<'a>(b: Box) -> &'a mut T 6 | { 7 | unsafe { &mut *Box::into_raw(b) } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/local_fn.rs: -------------------------------------------------------------------------------- 1 | fn fib(n: usize) -> usize { 2 | let mut cache = vec![0; n + 1]; 3 | return fib_memo(&mut cache, n); 4 | 5 | fn fib_memo(cache: &mut Vec, n: usize) -> usize { 6 | if n == 0 || n == 1 { 7 | return 1; 8 | } 9 | if cache[n] == 0 { 10 | cache[n] = 11 | fib_memo(cache, n - 1) + fib_memo(cache, n - 2); 12 | } 13 | cache[n] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/local_mod.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | mod m { 3 | pub(super) fn hello() { 4 | println!("Hello, world!"); 5 | } 6 | } 7 | m::hello(); 8 | } 9 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/main.rs: -------------------------------------------------------------------------------- 1 | enum X { 2 | X(T), Y 3 | } 4 | 5 | const C: X = X::Y; 6 | 7 | fn main() {} 8 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/make_mut.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | #[derive(Clone)] 4 | enum Stack { 5 | Empty, 6 | Cons(T, Rc>) 7 | } 8 | 9 | impl Stack { 10 | fn push(&mut self, value: T) { 11 | let this = std::mem::replace(self, Stack::Empty); 12 | *self = Stack::Cons(value, Rc::new(this)); 13 | } 14 | 15 | fn pop(&mut self) -> Option { 16 | match self { 17 | Stack::Empty => None, 18 | Stack::Cons(value, tail) => { 19 | *self = tail; 20 | Some(value) 21 | } 22 | } 23 | } 24 | } 25 | 26 | fn main() {} 27 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/match.rs: -------------------------------------------------------------------------------- 1 | fn foo(x: i32) { 2 | const ZERO: i32 = 0; 3 | 4 | match x { 5 | ZERO => println!("zero"), // <1> 6 | other => println!("other: {}", other), // <2> 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/ns.rs: -------------------------------------------------------------------------------- 1 | const FOO: i32 = 92; // value 2 | type FOO = (); // type 3 | 4 | struct Bar { f: u32 } // type 5 | fn Bar(f: u32) -> Bar { // value 6 | Bar { f } 7 | } 8 | 9 | mod m {} // type 10 | fn m() {} // value 11 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/pi.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println!("π/2 = {}", FRAC_PI_2); 3 | 4 | const PI: f32 = 3.1415926; 5 | const FRAC_PI_2: f32 = PI / 2.0; 6 | } 7 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/print_local.rs: -------------------------------------------------------------------------------- 1 | fn foo() { 2 | let x = 92; 3 | // closure may outlive the current function, but it borrows `x`, which is owned by the current function 4 | std::thread::spawn(|| { 5 | println!("{}", x); 6 | }); 7 | } 8 | 9 | fn main() {} 10 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/rc.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | fn main() { 4 | let hello1: Rc = Rc::new("hello".to_string()); 5 | let hello2 = Rc::clone(&hello); 6 | } 7 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/ref_proj.rs: -------------------------------------------------------------------------------- 1 | use std::cell::{RefCell, Ref}; 2 | 3 | #[derive(Default)] 4 | struct Person { first_name: String, last_name: String } 5 | 6 | fn main() { 7 | let cell = RefCell::new(Person::default()); 8 | let p: Ref = cell.borrow(); 9 | let first_name: Ref = 10 | Ref::map(p, |it| it.first_name.as_str()); 11 | } 12 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/static_array.rs: -------------------------------------------------------------------------------- 1 | const HELLO: [u8; 5] = *b"hello"; 2 | const HELLO_2: [u8; 5] = [104, 101, 108, 108, 111]; 3 | 4 | fn main() { 5 | assert_eq!(HELLO, HELLO_2); 6 | } 7 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/static_counter.rs: -------------------------------------------------------------------------------- 1 | static COUNTER: usize = 0; 2 | 3 | fn main() { 4 | println!("{}", COUNTER); 5 | } 6 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/static_elision.rs: -------------------------------------------------------------------------------- 1 | const WORDS: &[&str] = &[ 2 | "hello", 3 | "world", 4 | ]; 5 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/static_lifetime.rs: -------------------------------------------------------------------------------- 1 | const X: i32 = 92; 2 | const R: &'static i32 = &X; 3 | 4 | fn foo(x: &i32) { 5 | println!("{}", x); 6 | } 7 | 8 | fn main() { 9 | foo(R); 10 | } 11 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/struct_sugar.rs: -------------------------------------------------------------------------------- 1 | struct S; // <1> 2 | 3 | struct T(u32, i32); // <2> 4 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/tag.rs: -------------------------------------------------------------------------------- 1 | trait Tagged { 2 | const TAG: &'static str; 3 | } 4 | 5 | struct Foo; 6 | impl Tagged for Foo { const TAG: &'static str = "Foo"; } 7 | 8 | struct Bar; 9 | impl Tagged for Bar { const TAG: &'static str = "Bar"; } 10 | 11 | 12 | fn by_tag(tag: &str) { 13 | match tag { 14 | Foo::TAG => println!("foo"), 15 | Bar::TAG => println!("bar"), 16 | _ => panic!("unknown tag: {:?}", tag) 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/code/upper.rs: -------------------------------------------------------------------------------- 1 | fn uppercase(data: &str) -> &'static str { 2 | // unicode, размер новой строки может быть больше, 3 | // in-place uppercase не работает 4 | let data: String = data.to_uppercase(); 5 | 6 | // убрали поле capacity 7 | let data: Box = data.into_boxed_str(); 8 | 9 | // Вызвали "метод" 10 | Box::leak(data) 11 | } 12 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/slides.adoc: -------------------------------------------------------------------------------- 1 | = Rust 2019 2 | Алексей Кладов 3 | :icons: font 4 | :lecture: Лекция 9: Умные Указатели, static 5 | :table-caption!: 6 | :example-caption!: 7 | 8 | [.title-slide] 9 | == const 10 | 11 | == const 12 | 13 | [source,rust] 14 | ---- 15 | include::code/const.rs[] 16 | ---- 17 | 18 | [.language-rust]`const` -- конструкция верхнего уровня 19 | 20 | Тип нужно указывать явно 21 | 22 | == const 23 | 24 | TIP: Байты [.language-rust]`const` находятся в `.text` (неизменяемой) секции 25 | исполняемого файла 26 | 27 | Константа вычисляется во время компиляции 28 | 29 | Можно использовать арифметику, литералы, простые выражения и 30 | [.language-rust]`const fn` функции 31 | 32 | [source,rust] 33 | ---- 34 | impl Cell { 35 | pub const fn new(value: T) -> Cell { 36 | Cell { 37 | value: UnsafeCell::new(value), 38 | } 39 | } 40 | ---- 41 | 42 | == match 43 | 44 | .Константы можно использовать в [.language-rust]`match`: 45 | [source,rust] 46 | ---- 47 | include::code/match.rs[] 48 | ---- 49 | 50 | <1> сравнили `x` c нулём 51 | <2> записали `x` в `other` 52 | 53 | WARNING: Семантика зависит от наличия константы в области видимости 54 | 55 | == ! 56 | 57 | .[.language-rust]`const` можно использовать в [.language-rust]`impl`: 58 | [source,rust] 59 | ---- 60 | include::code/tag.rs[] 61 | ---- 62 | 63 | [.title-slide] 64 | == Области Видимости 65 | 66 | == Видимость 67 | 68 | Константы (как и любые конструкции верхнего уровня) можно объявлять внутри блока 69 | 70 | Для них создаётся невидимый модуль, порядок объявления не важен 71 | 72 | [source,rust] 73 | ---- 74 | include::code/pi.rs[] 75 | ---- 76 | 77 | == Локальные Функции 78 | 79 | .Функцию тоже можно объявить внутри блока, но она не будет замыканием: 80 | 81 | [source,rust] 82 | ---- 83 | include::code/local_fn.rs[] 84 | ---- 85 | 86 | == Локальный Мир 87 | 88 | .Модули тоже можно объявлять локально: 89 | 90 | [source,rust] 91 | ---- 92 | include::code/local_mod.rs[] 93 | ---- 94 | 95 | == Пространства имён 96 | 97 | В Rust есть два непересекающихся пространства имён (a-la Lips-2): "типы" и 98 | "значения": 99 | 100 | [source,rust] 101 | ---- 102 | include::code/ns.rs[] 103 | ---- 104 | 105 | На практике коллизий мало, из-за разных соглашений об именовании 106 | 107 | == Пространства имён 108 | 109 | Unit и tuple структуры рассахариваются в определение типа и функции/константы 110 | 111 | [source,rust] 112 | ---- 113 | include::code/struct_sugar.rs[] 114 | ---- 115 | 116 | <1> создали тип `S` и [.language-rust]`const S: S` 117 | <2> создали тип `T` и [.language-rust]`+fn T(_0: u32, _1: i32) -> T+` 118 | 119 | `None` -- константа типа `Option` 120 | 121 | `Some` -- функция типа [.language-rust]`+fn(T) -> Option+` 122 | 123 | [.title-slide] 124 | == 'static 125 | 126 | == 'static 127 | 128 | На константы можно брать ссылку 129 | 130 | вж -- [.language-rust]`'static`, больше любого другого времени жизни 131 | 132 | [source,rust] 133 | ---- 134 | include::code/static_lifetime.rs[] 135 | ---- 136 | 137 | == Lifetime Elision 138 | 139 | В константах работает lifetime elision, результат -- [.language-rust]`'static` 140 | 141 | [source,rust] 142 | ---- 143 | include::code/static_elision.rs[] 144 | ---- 145 | 146 | Тип строковго литерала -- [.language-rust]`&'static str` 147 | 148 | Тип байтового литерала -- [.language-rust]`&'static [u8; _]` 149 | 150 | [source,rust] 151 | ---- 152 | include::code/static_array.rs[] 153 | ---- 154 | 155 | == Строковые литералы 156 | 157 | [source,rust,subs="+quotes"] 158 | ---- 159 | "hello": &'static str // байты в utf-8 160 | b"hello": &'static [u8; 5] // байты в ASCII 161 | 162 | 'a': char // 32 бита 163 | b'a': u8 164 | 165 | "hello\nworld" // обычное экранирование через \ 166 | [hl-string]##"hello 167 | world"## // многострочный литерал 168 | 169 | [hl-string]##"hello \## // \ убирает \n и пробелы после него 170 | [hl-string]##world"## 171 | 172 | r"hello\nworld" // сырой литерал, \ это \ 173 | ---- 174 | [source] 175 | ---- 176 | r###"raw literal with "## inside!"### 177 | ---- 178 | 179 | == [] 180 | 181 | У `[]` тип [.language-rust]`&'static []`, пустые слайсы можно извлекать из 182 | воздуха 183 | 184 | [source,rust] 185 | ---- 186 | fn as_slice<'a>(xs: Option<&'a Vec>) -> &'a [i32] { 187 | match xs { 188 | Some(xs) => xs.as_slice(), 189 | None => &[], 190 | } 191 | } 192 | ---- 193 | 194 | == Box::leak 195 | 196 | Ещё один способ получить вж [.language-rust]`'static` это пообещать, что память 197 | никогда не будет освобождена 198 | 199 | [source,rust] 200 | ---- 201 | include::code/leak.rs[] 202 | ---- 203 | 204 | * после вызова `into_raw` память не будет освобождена 205 | 206 | * можно получить *любое* вж (но [.language-rust]`'static` -- самый интересный случай) 207 | 208 | == dyn T + 'static 209 | 210 | .У [.language-rust]`dyn T` типов тоже есть lifetime: 211 | 212 | [source,rust] 213 | ---- 214 | include::code/dyn.rs[] 215 | ---- 216 | 217 | * [.language-rust]`Box == Box` 218 | * [.language-rust]`&'a dyn T == &'a (dyn T + 'a)` 219 | * [.language-rust]`dyn T == dyn T +'static` 220 | 221 | [.title-slide] 222 | == static 223 | 224 | == static 225 | 226 | [.language-rust]`const` пишет в .text секцию, [.language-rust]`static` -- в 227 | изменяемую .data секцию 228 | 229 | [source,rust] 230 | ---- 231 | include::code/static_counter.rs[] 232 | ---- 233 | 234 | == static mut 235 | 236 | [source,rust,subs="+quotes"] 237 | ---- 238 | static mut COUNTER: usize = 0; 239 | 240 | fn main() { 241 | [hl-error]##COUNTER += 1##; // use of mutable static is unsafe 242 | println!("{}", [hl-error]##COUNTER##); // and requires unsafe function 243 | } // or block 244 | ---- 245 | 246 | Использовать [.language-rust]`static mut` так просто нельзя... 247 | 248 | == static mut 249 | 250 | [source,rust] 251 | ---- 252 | static mut X: usize = 0; 253 | 254 | pub fn sneaky() -> &'static mut usize { 255 | &mut X 256 | } 257 | 258 | fn main() { 259 | let x1: &mut usize = sneaky(); 260 | let x2: &mut usize = sneaky(); // алиасинг! 261 | } 262 | ---- 263 | 264 | При операциях со [.language-rust]`static mut`, вызывающий код должен 265 | гарантировать, что не создаётся алиасинг 266 | 267 | NOTE: Глобальные переменные доступны всем (`&`) 268 | 269 | == static + interior mutability 270 | 271 | [source,rust] 272 | ---- 273 | use std::cell::Cell; 274 | 275 | static COUNTER: Cell = Cell::new(0); 276 | 277 | fn main() { 278 | COUNTER.set(COUNTER.get() + 1); 279 | println!("{}", COUNTER.get()); 280 | } 281 | ---- 282 | 283 | Казалось бы, должно работать: не врём про [.language-rust]`mut` 284 | 285 | Не работает из-за многопоточности 286 | 287 | CAUTION: В Rust глобальные переменные имеют _объективные_ недостатки 288 | 289 | == Life Before Main 290 | 291 | Так же, как и [.language-rust]`const`, [.language-rust]`static` это просто байты 292 | в бинарном файле 293 | 294 | NOTE: [.language-rust]`static` можно инициализировать только константой 295 | 296 | В C++, Java, Swift etc глобальную переменную можно инициализировать чем угодно, 297 | инициализация происходит при первом обращении или до main 298 | 299 | В Rust нет жизни до `main`, ленивую инициализацию надо писать явно (a-la 300 | `OnceCell`). 301 | 302 | [.title-slide] 303 | == Умные Указатели 304 | 305 | == Owning Smart Pointers 306 | 307 | Умный указатель управляет доступом к ресурсу 308 | 309 | [.language-rust]`Box` -- главный умный указатель, управляет памятью, вызывает 310 | `free` в `drop` 311 | 312 | [.language-rust]`Vec` -- тоже умный указатель, `Box` для нескольких значений 313 | 314 | NOTE: `Vec` это не просто коллекция, а стратегия управления памятью 315 | 316 | == Borrowing Smart Pointers 317 | 318 | [.language-rust]`cell::Ref<'a, T>` и [.language-rust]`cell::RefMut<'a, T>` -- 319 | умные заимствующие указатели 320 | 321 | Оборачивают `&T` / [.language-rust]`&mut T` и в `Drop` уменьшают счётчик ссылок 322 | на `RefCell` 323 | 324 | == Deref 325 | 326 | .Умные указатели реализуют `Deref` / `DerefMut`: 327 | * [.language-rust]`impl Deref for Vec` 328 | * [.language-rust]`impl DerefMut for Vec` 329 | * [.language-rust]`impl<'a, T> Deref for Ref<'a, T>` 330 | * ... 331 | 332 | == ! 333 | .Паттерн: ассоциированная функция лучше метода 334 | [source,rust,subs="+quotes"] 335 | ---- 336 | impl Box { 337 | fn leak<'a>(##b:## Box) -> &'a T { ... } 338 | } 339 | ---- 340 | 341 | [source,rust] 342 | ---- 343 | include::code/upper.rs[] 344 | ---- 345 | 346 | Метод мог бы конфликтовать с методом на типе (`pipe.leak()`) 347 | 348 | == ! 349 | 350 | [source,rust] 351 | ---- 352 | impl<'a, T> Ref<'a, T> { 353 | pub fn map(orig: Ref<'a, T>, f: F) -> Ref<'a, U> 354 | where 355 | F: FnOnce(&T) -> &U, 356 | U: ?Sized, 357 | } 358 | ---- 359 | 360 | .`Ref::map` -- проекция: 361 | [source,rust] 362 | ---- 363 | include::code/ref_proj.rs[] 364 | ---- 365 | 366 | == std::rc::Rc 367 | 368 | `Rc`:: 369 | **R**eference **C**ounting owning smart pointer 370 | 371 | `Rc` это Box с `O(1)` клонированием ([.language-rust]`impl Clone for Rc`) 372 | 373 | [source,rust] 374 | ---- 375 | include::code/rc.rs[] 376 | ---- 377 | 378 | `Rc` реализует `Deref`, но не `DerefMut`: `Rc` разделяется между 379 | несколькими владельцами, уникальной ссылки быть не может 380 | 381 | == std::rc::Rc 382 | 383 | Внутри лежит `RcBox` 384 | 385 | [source,rust] 386 | ---- 387 | struct RcBox { 388 | strong: Cell, 389 | weak: Cell, 390 | value: T, 391 | } 392 | ---- 393 | 394 | * Нужен `Cell`: `clone` получает `&` ссылку, но мы должны увеличить счётчик 395 | ссылок. 396 | * Значение и счётчик ссылок всегда аллоцированны вместе => нет необходимости в `std::make_shared` 397 | 398 | == std::rc::Rc::make_mut 399 | 400 | [source,rust] 401 | ---- 402 | impl Rc { 403 | pub fn make_mut(this: &mut Self) -> &mut T { ... } 404 | } 405 | ---- 406 | 407 | `make_mut` -- магический метод, если rc == 1, то возвращаем ссылку на данные, 408 | иначе клонируем их 409 | 410 | Так как [.language-rust]`this: &mut`, то не может быть других `&Rc` 411 | 412 | Можно писать неизменяемые структуры данных, поддерживающие in-place модификацию (clone-on-write) 413 | 414 | == ! 415 | 416 | .В функциональных языках: 417 | [source] 418 | ---- 419 | insert :: set -> a -> set 420 | ---- 421 | 422 | .В Rust: 423 | [source,rust] 424 | ---- 425 | 426 | #[derive(Clone)] // O(1) 427 | struct Set { /* Rc */ } 428 | 429 | impl Set { 430 | fn insert(&mut self, value: T) { ... } 431 | } 432 | ---- 433 | 434 | - из-за [.language-rust]`&mut` `Vec` так же надёжен, как и cons-list 435 | - можно менять `std::Vec` на `im::Vector` без изменения API! 436 | - http://smallcultfollowing.com/babysteps/blog/2018/02/01/in-rust-ordinary-vectors-are-values/ 437 | - https://docs.rs/im/ 438 | 439 | 440 | == std::borrow::Cow 441 | 442 | .`Cow` хранит либо ссылку с вж [.language-rust]`'a`, либо значение: 443 | [source,rust] 444 | ---- 445 | pub enum Cow<'a, B> 446 | where 447 | B: ToOwned + ?Sized + 'a, 448 | { 449 | Borrowed(&'a B), 450 | Owned(::Owned), 451 | } 452 | 453 | pub trait ToOwned { 454 | type Owned: Borrow; 455 | 456 | fn to_owned(&self) -> Self::Owned; 457 | } 458 | 459 | pub trait Borrow { 460 | fn borrow(&self) -> &Borrowed; 461 | } 462 | ---- 463 | 464 | == ToOwned 465 | 466 | `X: Borrow` -- из `X` можно получить `&Y`, согласованную по `Eq`, `Hash`, `Ord` 467 | 468 | `ToOwned` -- противоположность `Borrow`, из ссылки получаем значение 469 | 470 | [source,rust] 471 | ---- 472 | impl ToOwned for str { 473 | type Owned = String; 474 | } 475 | 476 | impl ToOwned for [T] { 477 | type Owned = Vec; 478 | } 479 | ---- 480 | 481 | == Cow 482 | 483 | [source,rust] 484 | ---- 485 | include::code/cow_lower.rs[] 486 | ---- 487 | 488 | 489 | Если `s` и так в нижнем регистре -- экономим аллокацию 490 | 491 | == ! 492 | 493 | [source,rust] 494 | ---- 495 | pub fn search_case_insensitive(s: &str) -> bool { 496 | let s: String = s.to_lowercase(); // аллоцируем всегда 497 | search_lowercased(&s); 498 | } 499 | 500 | fn search_lowercased(s: &str) -> bool { 501 | ... 502 | } 503 | ---- 504 | 505 | Хотим написать поиск без учёта регистра. Для этого приводим и данные, и паттерн 506 | в нижний регистр 507 | 508 | 509 | == ! 510 | 511 | [source,rust,subs="+quotes"] 512 | ---- 513 | pub fn search_case_insensitive(s: &str) -> bool { 514 | let s: &str = if is_lowercase(s) { 515 | s 516 | } else { 517 | [hl-error]##&s.to_lowercase()## // ссылка на локальную переменную 518 | }; 519 | search_lowercased(&s); 520 | } 521 | 522 | fn search_lowercased(s: &str) -> bool { 523 | ... 524 | } 525 | ---- 526 | 527 | == ! 528 | 529 | [source,rust,subs="+quotes"] 530 | ---- 531 | pub fn search_case_insensitive(s: &str) -> bool { 532 | if is_lowercase(s) { 533 | search_lowercased(s) // немного дублирования 534 | } else { 535 | search_lowercased(&s.to_lowercase()) 536 | } 537 | } 538 | 539 | fn search_lowercased(s: &str) -> bool { 540 | ... 541 | } 542 | ---- 543 | 544 | == ! 545 | 546 | [source,rust,subs="+quotes"] 547 | ---- 548 | pub fn search_case_insensitive(s: &str) -> bool { 549 | let s: Cow = to_lowercase(s); // нет аллокации 550 | search_lowercased(&*s); // runtime проверка варианта при Deref 551 | } 552 | 553 | fn search_lowercased(s: &str) -> bool { 554 | ... 555 | } 556 | ---- 557 | 558 | == ! 559 | 560 | [source,rust,subs="+quotes"] 561 | ---- 562 | pub fn search_case_insensitive(s: &str) -> bool { 563 | let lowercased: String; 564 | let s: &str = if is_lowercase(s) { 565 | s 566 | } else { 567 | lowercased = s.to_lowercase(); 568 | &lowercased 569 | }; 570 | search_lowercased(&s); 571 | } 572 | 573 | fn search_lowercased(s: &str) -> bool { 574 | ... 575 | } 576 | ---- 577 | 578 | TIP: Всегда можно расширить время жизни локальной переменной до всей функции, не 579 | обязательно её инициализировать 580 | 581 | 582 | -------------------------------------------------------------------------------- /lectures/09-smart-pointers/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/09-smart-pointers/slides.pdf -------------------------------------------------------------------------------- /lectures/10-multithreading/code/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "arrayvec" 5 | version = "0.4.10" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "cfg-if" 13 | version = "0.1.7" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | 16 | [[package]] 17 | name = "crossbeam" 18 | version = "0.7.1" 19 | source = "registry+https://github.com/rust-lang/crates.io-index" 20 | dependencies = [ 21 | "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 22 | "crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", 23 | "crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 24 | "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 25 | "crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", 26 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 27 | ] 28 | 29 | [[package]] 30 | name = "crossbeam-channel" 31 | version = "0.3.8" 32 | source = "registry+https://github.com/rust-lang/crates.io-index" 33 | dependencies = [ 34 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 35 | "smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)", 36 | ] 37 | 38 | [[package]] 39 | name = "crossbeam-deque" 40 | version = "0.7.1" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | dependencies = [ 43 | "crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 44 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 45 | ] 46 | 47 | [[package]] 48 | name = "crossbeam-epoch" 49 | version = "0.7.1" 50 | source = "registry+https://github.com/rust-lang/crates.io-index" 51 | dependencies = [ 52 | "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", 53 | "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 54 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 55 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 56 | "memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 57 | "scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", 58 | ] 59 | 60 | [[package]] 61 | name = "crossbeam-queue" 62 | version = "0.1.2" 63 | source = "registry+https://github.com/rust-lang/crates.io-index" 64 | dependencies = [ 65 | "crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", 66 | ] 67 | 68 | [[package]] 69 | name = "crossbeam-utils" 70 | version = "0.6.5" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | dependencies = [ 73 | "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", 74 | "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", 75 | ] 76 | 77 | [[package]] 78 | name = "examples" 79 | version = "0.0.0" 80 | dependencies = [ 81 | "crossbeam 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", 82 | ] 83 | 84 | [[package]] 85 | name = "lazy_static" 86 | version = "1.3.0" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | 89 | [[package]] 90 | name = "memoffset" 91 | version = "0.2.1" 92 | source = "registry+https://github.com/rust-lang/crates.io-index" 93 | 94 | [[package]] 95 | name = "nodrop" 96 | version = "0.1.13" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | 99 | [[package]] 100 | name = "scopeguard" 101 | version = "0.3.3" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | 104 | [[package]] 105 | name = "smallvec" 106 | version = "0.6.9" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | 109 | [metadata] 110 | "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" 111 | "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" 112 | "checksum crossbeam 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b14492071ca110999a20bf90e3833406d5d66bfd93b4e52ec9539025ff43fe0d" 113 | "checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" 114 | "checksum crossbeam-deque 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b18cd2e169ad86297e6bc0ad9aa679aee9daa4f19e8163860faf7c164e4f5a71" 115 | "checksum crossbeam-epoch 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "04c9e3102cc2d69cd681412141b390abd55a362afc1540965dad0ad4d34280b4" 116 | "checksum crossbeam-queue 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7c979cd6cfe72335896575c6b5688da489e420d36a27a0b9eb0c73db574b4a4b" 117 | "checksum crossbeam-utils 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f8306fcef4a7b563b76b7dd949ca48f52bc1141aa067d2ea09565f3e2652aa5c" 118 | "checksum lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bc5729f27f159ddd61f4df6228e827e86643d4d3e7c32183cb30a1c08f604a14" 119 | "checksum memoffset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0f9dc261e2b62d7a622bf416ea3c5245cdd5d9a7fcc428c0d06804dfce1775b3" 120 | "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" 121 | "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" 122 | "checksum smallvec 0.6.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c4488ae950c49d403731982257768f48fada354a5203fe81f9bb6f43ca9002be" 123 | -------------------------------------------------------------------------------- /lectures/10-multithreading/code/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "examples" 3 | version = "0.0.0" 4 | 5 | [dependencies] 6 | crossbeam = "0.7.1" 7 | 8 | [[bin]] 9 | name = "crossbeam_scope" 10 | path = "./crossbeam_scope.rs" 11 | 12 | [[bin]] 13 | name = "racy" 14 | path = "./racy.rs" 15 | 16 | [[bin]] 17 | name = "non-racy" 18 | path = "./non-racy.rs" 19 | -------------------------------------------------------------------------------- /lectures/10-multithreading/code/crossbeam_scope.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let x = 92; 3 | crossbeam::scope(|s| { 4 | s.spawn(|_| { // <1> 5 | println!("{}", x) 6 | }); 7 | }).unwrap(); 8 | } 9 | -------------------------------------------------------------------------------- /lectures/10-multithreading/code/move_vec.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let mut xs = vec![1, 2, 3]; 3 | std::thread::spawn(move || { 4 | xs.push(4); 5 | println!("{:?}", xs); 6 | }); 7 | } 8 | -------------------------------------------------------------------------------- /lectures/10-multithreading/code/non-racy.rs: -------------------------------------------------------------------------------- 1 | use std::sync::atomic::{AtomicUsize, Ordering}; 2 | 3 | fn main() { 4 | let mut counter = AtomicUsize::new(0); 5 | crossbeam::scope(|s| { 6 | s.spawn(|_| { 7 | counter.fetch_add(1, Ordering::SeqCst); 8 | }); 9 | counter.fetch_add(1, Ordering::SeqCst); 10 | }).unwrap(); 11 | assert_eq!(*counter.get_mut(), 2) 12 | } 13 | -------------------------------------------------------------------------------- /lectures/10-multithreading/code/racy.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | 3 | fn main() { 4 | let counter = Cell::new(0); 5 | crossbeam::scope(|s| { 6 | s.spawn(|_| { 7 | counter.set(counter.get() + 1) 8 | }); 9 | counter.set(counter.get() + 1); 10 | }).unwrap(); 11 | println!("{}", counter.get()); 12 | } 13 | -------------------------------------------------------------------------------- /lectures/10-multithreading/code/racy2.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | fn main() { 4 | let hello = Rc::new(String::from("hello")); 5 | let thread = std::thread::spawn({ 6 | let hello = Rc::clone(&hello); 7 | move || { 8 | println!("{}", hello) 9 | } 10 | }); 11 | thread.join().unwrap(); 12 | } 13 | -------------------------------------------------------------------------------- /lectures/10-multithreading/code/scope.rs: -------------------------------------------------------------------------------- 1 | pub fn library_fn T>(user_code: F) -> T { 2 | let _guard = Cleanup; // <1> 3 | user_code() 4 | } 5 | 6 | struct Cleanup; 7 | 8 | impl Drop for Cleanup { 9 | fn drop(&mut self) { 10 | println!("cleanup!") 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lectures/10-multithreading/code/spawn.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | fn main() { 4 | let handle: thread::JoinHandle = thread::spawn(|| { // <1> 5 | println!("Hello, world"); 6 | 92 7 | }); 8 | let value = handle.join().unwrap(); // <2> 9 | assert_eq!(value, 92); 10 | } 11 | -------------------------------------------------------------------------------- /lectures/10-multithreading/code/spawn_builder.rs: -------------------------------------------------------------------------------- 1 | use std::thread; 2 | 3 | fn main() { 4 | let handle = thread::Builder::new() 5 | .name("my-thread".into()) 6 | .stack_size(32 * 1024) 7 | .spawn(|| { 8 | println!("Hello, world"); 9 | 92 10 | }).unwrap(); 11 | let value = handle.join().unwrap(); 12 | assert_eq!(value, 92); 13 | } 14 | -------------------------------------------------------------------------------- /lectures/10-multithreading/code/spawn_move.rs: -------------------------------------------------------------------------------- 1 | fn foo() { 2 | let x = 92; 3 | std::thread::spawn(move || { 4 | println!("{}", x); 5 | }); 6 | } 7 | -------------------------------------------------------------------------------- /lectures/10-multithreading/mem.bob: -------------------------------------------------------------------------------- 1 | 0 2 | +--------+----------+- - - - -+-----------+- - - - -+-----------+- - - - -* 3 | | | \ | \ | \ 4 | | | Stack 1 / | Stack 2 / | Stack 3 / 5 | | | \ | \ | \ 6 | | | / | / | / 7 | +--------+----------+- - - - -+-----------+- - - - -+-----------+- - - - -* 8 | 9 | 10 | "R + W" "R + X" 11 | <- - - - - - -| |- - - - - - -| 12 | *- - - - -+---------------------------+--------+--------+---------+-------+ 13 | / | | | | | 14 | \ Heap | .bss | .data | .rodata | .text | 15 | / | | | | | 16 | \ | | | | | 17 | *- - - - -+---------------------------+--------+--------+---------+-------+ 18 | "2^64 - 1" 19 | -------------------------------------------------------------------------------- /lectures/10-multithreading/mem.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 0 169 | 170 | 171 | 172 | 173 | Stack 174 | 175 | 176 | 177 | 178 | 1 179 | 180 | 181 | 182 | 183 | Heap 184 | 185 | 186 | 187 | 188 | Stack 189 | 190 | 191 | 192 | 193 | 2 194 | 195 | 196 | 197 | 198 | .bss 199 | 200 | 201 | 202 | 203 | R + W 204 | 205 | 206 | 207 | 208 | .data 209 | 210 | 211 | 212 | 213 | Stack 214 | 215 | 216 | 217 | 218 | .rodata 219 | 220 | 221 | 222 | 223 | 3 224 | 225 | 226 | 227 | 228 | R + X 229 | 230 | 231 | 232 | 233 | 2^64 - 1 234 | 235 | 236 | 237 | 238 | .text 239 | 240 | 241 | -------------------------------------------------------------------------------- /lectures/10-multithreading/model.bob: -------------------------------------------------------------------------------- 1 | | 2 | +------------+ | 3 | +--+"data = foo"| | 4 | | +-----+------+ | 5 | | Program Order | 6 | | v | 7 | +-------------------------+ | 8 | | |"is_locked.store(false)" |-. | 9 | +-------------------------+ \| +-----------------------------------------+ 10 | | `----> |"is_locked.compare_and_swap(false, true)"| 11 | | |SW +-----+-----------------------------------+ 12 | +- - - - - - - - -. | | PO 13 | Happens Before \ | V 14 | \ | +---------------+ 15 | `- - - - - - - - ->| tmp = data | 16 | | +---------------+ 17 | | 18 | | 19 | | 20 | 21 | 22 | -------------------------------------------------------------------------------- /lectures/10-multithreading/model.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | Happens 142 | 143 | 144 | 145 | 146 | data = foo 147 | 148 | 149 | 150 | 151 | is_locked.store(false) 152 | 153 | 154 | 155 | 156 | Before 157 | 158 | 159 | 160 | 161 | Program 162 | 163 | 164 | 165 | 166 | Order 167 | 168 | 169 | 170 | 171 | SW 172 | 173 | 174 | 175 | 176 | is_locked.compare_and_swap(false, true) 177 | 178 | 179 | 180 | 181 | tmp 182 | 183 | 184 | 185 | 186 | PO 187 | 188 | 189 | 190 | 191 | data 192 | 193 | 194 | -------------------------------------------------------------------------------- /lectures/10-multithreading/scope: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/10-multithreading/scope -------------------------------------------------------------------------------- /lectures/10-multithreading/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/10-multithreading/slides.pdf -------------------------------------------------------------------------------- /lectures/11-unsafe/code/dropck.rs: -------------------------------------------------------------------------------- 1 | struct S { 2 | t: T, 3 | } 4 | 5 | impl Drop for S { 6 | fn drop(&mut self) {} 7 | } 8 | 9 | fn main() { 10 | let (s, x): (S<_>, i32); 11 | x = 92; 12 | s = S { t: &x }; 13 | drop(s) 14 | } 15 | -------------------------------------------------------------------------------- /lectures/11-unsafe/code/evil.rs: -------------------------------------------------------------------------------- 1 | fn assign_ref<'a>(r: &mut &'a str, s: &'a str) { 2 | *r = s 3 | } 4 | 5 | fn evil(r: &mut &'static str) { 6 | let local: String = "spam".to_string(); 7 | let local: &str = local.as_str(); 8 | assign_ref(r, local) 9 | } 10 | 11 | fn main() { 12 | let mut hello: &'static str = "hello"; 13 | evil(&mut hello); 14 | println!("{}", hello); 15 | } 16 | -------------------------------------------------------------------------------- /lectures/11-unsafe/code/get_pair_mut.rs: -------------------------------------------------------------------------------- 1 | fn get_pair_mut( 2 | xs: &mut [T], 3 | idx1: usize, 4 | idx2: usize, 5 | ) -> (&mut T, &mut T) { 6 | 7 | let x1: *mut T = &mut xs[idx1]; 8 | let x2: *mut T = &mut xs[idx2]; 9 | 10 | unsafe { 11 | assert!(idx1 != idx2); 12 | (&mut *x1, &mut *x2) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /lectures/11-unsafe/code/get_pair_mut2.rs: -------------------------------------------------------------------------------- 1 | fn get_pair_mut( 2 | xs: &mut [T], 3 | idx1: usize, 4 | idx2: usize, 5 | ) -> (&mut T, &mut T) { 6 | let (idx1, idx2) = (idx1.min(idx2), idx1.max(idx2)); 7 | let (lo, hi) = xs.split_at_mut(idx2); 8 | (&mut lo[idx1], &mut hi[0]) 9 | } 10 | -------------------------------------------------------------------------------- /lectures/11-unsafe/code/phantom.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | struct IterMut<'a, T> { 4 | begin: *mut T, 5 | end: *mut T, 6 | slice: PhantomData<&'a mut [T]>, 7 | } 8 | 9 | impl<'a, T> IterMut<'a, T> { 10 | fn new(slice: &'a mut [T]) -> Self { 11 | assert!(std::mem::size_of::() > 0); 12 | let begin = slice as *mut [T] as *mut T; 13 | let end = unsafe { begin.add(slice.len()) }; 14 | IterMut { 15 | begin, 16 | end, 17 | slice: PhantomData, 18 | } 19 | } 20 | } 21 | 22 | 23 | impl<'a, T> Iterator for IterMut<'a, T> { 24 | type Item = &'a mut T; 25 | 26 | fn next(&mut self) -> Option<&'a mut T> { 27 | if self.begin == self.end { 28 | return None; 29 | } 30 | let curr = self.begin; 31 | unsafe { 32 | self.begin = self.begin.add(1); 33 | Some(&mut *curr) 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lectures/11-unsafe/slides.adoc: -------------------------------------------------------------------------------- 1 | = Rust 2019 2 | Алексей Кладов 3 | :icons: font 4 | :lecture: Лекция 11: unsafe 5 | :table-caption!: 6 | :example-caption!: 7 | 8 | == ! 9 | 10 | image::unsafe-ferris.png[] 11 | 12 | [.centered] 13 | == ! 14 | 15 | .В любом языке нужны низкоуровневые, "системные" операции: 16 | * взаимодействие с OS 17 | * управление памятью 18 | * быстрые коллекции 19 | 20 | [.centered] 21 | == Где Живёт Интересный Код? 22 | 23 | * в runtime языка (Go) / C-расширениях (Python) 24 | 25 | .Нужно убедится, что расширение: 26 | . даёт правильный результат при правильном использовании 27 | . даёт ошибку при неправильном использовании (нет UB) 28 | 29 | --- 30 | 31 | * написан на самом языке (C/C++) 32 | 33 | Язык должен быть достаточно низкоуровневым, сложно отделить "runtime" от 34 | бизнесс-логики 35 | 36 | == Rust 37 | 38 | В Rust [.language-rust]`unsafe` выполняет роль C-расширений 39 | 40 | 41 | .Ядро языка: 42 | 43 | * [.language-rust]`*const T`, [.language-rust]`*mut T` (разыменовывание -- [.language-rust]`unsafe`) 44 | * [.language-rust]`extern "C" fn malloc(usize);` (вызов -- [.language-rust]`unsafe`) 45 | * [.language-rust]`&T`, [.language-rust]`&mut T`, move semantics, alias analysis 46 | 47 | .Стандартная библиотека: 48 | 49 | * `Box`, `Vec`, `HashMap` 50 | * `stdio`, `println!` 51 | 52 | == Rust 53 | 54 | [.language-rust]`unsafe` это "эффект": [.language-rust]`unsafe` операцию можно 55 | выполнить только из [.language-rust]`unsafe` функции 56 | 57 | Внутри safe функции можно написать [.language-rust]`unsafe` блок, но нужно 58 | гарантировать, что не возникнет UB 59 | 60 | https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/ 61 | 62 | == unsafe операции 63 | 64 | . разыменование сырого указателя 65 | . вызов [.language-rust]`unsafe` функции 66 | . [.language-rust]`impl` для [.language-rust]`unsafe trait` 67 | . доступ к [.language-rust]`static mut` 68 | . доступ к полям [.language-rust]`union` 69 | 70 | 71 | == UB 72 | 73 | . разыменование висящего/нулевого указателя 74 | . чтение не инициализированной памяти 75 | . нарушение правил алиасинга (создание перекрывающихся [.language-rust]`&mut`) 76 | . создание невалидных примитивных значений: нулевые/висящие ссылки, нулевые 77 | указатели на функцию, 78 | [.language-rust]`bool` отличный от 0 или 1, невалидный вариант 79 | [.language-rust]`enum`, невалидный [.language-rust]`char` (например, 80 | одиночный суррогат), не UTF-8 `str` 81 | . разматывание стэка через FFI 82 | . гонка данных 83 | 84 | [.centered] 85 | == Центральное Обещание Rust 86 | 87 | [.lead] 88 | Используя систему типов, можно писать safe функции, вызывающие внутри 89 | [.language-rust]`unsafe` операции, но **гарантирующие** отсутствие UB для любых 90 | значений параметров 91 | 92 | [.lead] 93 | [.language-rust]`unsafe` это не инструмент выхода за пределы языка, это основание! 94 | 95 | == Пример 96 | 97 | [source,rust] 98 | ---- 99 | fn get_pair_mut( 100 | xs: &mut [T], 101 | idx1: usize, 102 | idx2: usize, 103 | ) -> (&mut T, &mut T) 104 | ---- 105 | 106 | 107 | Функция `get_pair_mut` принимает массив, пару индексов и возвращает уникальные 108 | ссылки на соответсвующие элементы 109 | 110 | == Пример 111 | 112 | [source,rust] 113 | ---- 114 | include::code/get_pair_mut.rs[] 115 | ---- 116 | 117 | Вызов `get_pair_mut` с **любыми** `idx1` и `idx2` не может вызвать UB (но может привести к панике) 118 | 119 | == Пример 120 | 121 | [source,rust] 122 | ---- 123 | /// Safety: 124 | /// idx1 and idx2 must not be equal 125 | unsafe fn get_pair_mut( 126 | xs: &mut [T], 127 | idx1: usize, 128 | idx2: usize, 129 | ) -> (&mut T, &mut T) { 130 | 131 | let x1: *mut T = &mut xs[idx1]; 132 | let x2: *mut T = &mut xs[idx2]; 133 | 134 | (&mut *x1, &mut *x2) 135 | } 136 | ---- 137 | 138 | `assert` убрать можно, но тогда мы **обязаны** пометить функцию как [.language-rust]`unsafe` 139 | 140 | == Пример 141 | 142 | [source,rust] 143 | ---- 144 | include::code/get_pair_mut2.rs[] 145 | ---- 146 | 147 | TIP: Написав одну функцию с [.language-rust]`unsafe` (`split_at_mut`), другие можно 148 | выразить через неё, и получить memory safety бесплатно 149 | 150 | == Параметризованные функции 151 | 152 | [source,rust] 153 | ---- 154 | impl [T] { 155 | fn sort(&mut self) 156 | where 157 | T: Ord, 158 | } 159 | ---- 160 | 161 | Функция `sort` может использовать [.language-rust]`unsafe` внутри (например, 162 | чтобы убрать проверку выхода за границу массива) 163 | 164 | Для любых `T`, `sort` должна гарантировать отсутствие UB (потому что не помечена 165 | [.language-rust]`unsafe`) 166 | 167 | NOTE: Даже если реализация `T: Ord` не корректна (например, нет транзитивности), 168 | UB возникнуть не должно! 169 | 170 | [.centered] 171 | == Параметризованные функции 172 | 173 | Для сравнения, В C++ вызов `std::sort` для типа с не-транзитивным `<` это UB (и 174 | может привести к выходу за границу массива на практике) 175 | 176 | Баг в `+<=>+` в modern C++ может привести к UB: весь код `unsafe` 177 | 178 | 179 | В Rust [.language-rust]`unsafe` позволяет установить **границы** возможного UB 180 | 181 | == Параметризованные функции 182 | 183 | Возможные альтернативы: 184 | 185 | [source,rust] 186 | ---- 187 | impl [T] { 188 | /// Safety: 189 | /// Ord must be a correct total order for T 190 | unsafe fn sort_faster(&mut self) 191 | where 192 | T: Ord, 193 | } 194 | ---- 195 | 196 | Проверка контракта -- задача вызывающего `sort_faster` 197 | 198 | 199 | == Параметризованные функции 200 | 201 | Возможные альтернативы: 202 | 203 | [source,rust] 204 | ---- 205 | /// Safety: 206 | /// Implementors guarantee that Ord is a correct total order 207 | unsafe trait TrustedOrd: Ord {} 208 | 209 | impl [T] { 210 | fn sort_faster(&mut self) 211 | where 212 | T: TrustedOrd, 213 | } 214 | ---- 215 | 216 | Проверка контракта -- задача автора типа `T` 217 | 218 | == Send & Sync 219 | 220 | Send и Sync -- интересные [.language-rust]`unsafe trait` 221 | 222 | Автор [.language-rust]`unsafe impl Sync for T` обязан гарантировать отсутствие 223 | гонок данных 224 | 225 | В большинстве случаев автор -- компилятор 226 | 227 | Если все компоненты типа `Sync`, то сам тип тривиально `Sync` 228 | 229 | [.title-slide] 230 | == Напишем std 231 | 232 | 233 | [.centered] 234 | == Show Me the Code 235 | 236 | https://github.com/rust-lang/rust/blob/bfb443eb1de484fde141fa9090a9f4291cbe60a5/ 237 | 238 | Нас будет интересовать `src/libcore` 239 | 240 | 241 | == `[T]` 242 | 243 | .`libcore/slice/mod.rs`: 244 | [source,rust] 245 | ---- 246 | #[repr(C)] 247 | struct FatPtr { 248 | data: *const T, 249 | len: usize, 250 | } 251 | ---- 252 | 253 | `FatPtr` -- представление `&[T]` в runtime: пара из указателя и длины 254 | 255 | == `[T]` 256 | 257 | .`libcore/slice/mod.rs`: 258 | [source,rust] 259 | ---- 260 | #[repr(C)] 261 | union Repr<'a, T: 'a> { 262 | rust: &'a [T], 263 | rust_mut: &'a mut [T], 264 | raw: FatPtr, 265 | } 266 | ---- 267 | 268 | C-style, [.language-rust]`unsafe` union: содержит одно из полей, без тэга 269 | 270 | Доступ к полю [.language-rust]`union` -- [.language-rust]`unsafe` операция 271 | 272 | Изначально исключительно для FFI, но нашёл применение в [.language-rust]`unsafe` коде 273 | 274 | В данном случае: способ получить `FatPtr` из `&T` 275 | 276 | == `[T]` 277 | 278 | [source,rust] 279 | ---- 280 | #[repr(C)] 281 | union Repr<'a, T: 'a> { 282 | rust: &'a [T], 283 | rust_mut: &'a mut [T], 284 | raw: FatPtr, 285 | } 286 | 287 | impl [T] { 288 | pub const fn len(&self) -> usize { 289 | unsafe { 290 | Repr { rust: self } // <1> 291 | .raw // <2> 292 | .len 293 | } 294 | } 295 | ---- 296 | 297 | <1> скоструировали `Repr` из `[T]` 298 | <2> получили `FatPtr` 299 | 300 | == ! 301 | 302 | 303 | [source,rust] 304 | ---- 305 | use std::mem::{size_of, align_of}; 306 | 307 | pub unsafe fn from_raw_parts<'a, T>( 308 | data: *const T, 309 | len: usize, 310 | ) -> &'a [T] { 311 | debug_assert!( 312 | data as usize % align_of::() == 0, 313 | "attempt to create unaligned slice", 314 | ); 315 | debug_assert!( 316 | size_of::().saturating_mul(len) <= isize::MAX as usize, 317 | "attempt to create slice covering half the address space", 318 | ); 319 | Repr { raw: FatPtr { data, len } }.rust 320 | } 321 | ---- 322 | 323 | Констуктор слайсов: сделали `FatPtr`, посмотрели как на `&[T]` 324 | 325 | == `[T]` 326 | 327 | Немного компиляторной магии для синтаксиса `[T]`, в остальном библиотечный код 328 | 329 | Представление -- `+repr(C)+` структура 330 | 331 | Каст между `&[T]` и `FatPtr` через [.language-rust]`union` 332 | 333 | Знаем, что слайсы представленны как `FatPtr`, потому что так работают 334 | `from_raw_parts` и `from_raw_parts_mut` 335 | 336 | == `[T]` 337 | 338 | [source,rust] 339 | ---- 340 | impl [T] { 341 | pub const fn as_ptr(&self) -> *const T { 342 | self as *const [T] as *const T // <1> 343 | } 344 | } 345 | ---- 346 | 347 | <1> получить указатель можно без [.language-rust]`unsafe` (тоже магия компилятора)! 348 | 349 | == `[T]` 350 | 351 | [source,rust] 352 | ---- 353 | pub fn get>(&self, index: I) 354 | -> Option<&I::Output> 355 | { 356 | index.get(self) 357 | } 358 | 359 | pub fn get_mut>(&mut self, index: I) 360 | -> Option<&mut I::Output> 361 | { 362 | index.get_mut(self) 363 | } 364 | ---- 365 | 366 | Индексация -- через вспомогательный трейт `SliceIndex`, чтобы работали `.get(0)` 367 | и `.get(0..10)` 368 | 369 | == ! 370 | 371 | [source,rust] 372 | ---- 373 | impl SliceIndex<[T]> for usize { 374 | type Output = T; 375 | fn get(self, slice: &[T]) -> Option<&T> { 376 | if self < slice.len() { 377 | unsafe { Some(self.get_unchecked(slice)) } 378 | } else { 379 | None 380 | } 381 | } 382 | unsafe fn get_unchecked(self, slice: &[T]) -> &T { 383 | &*slice.as_ptr().add(self) 384 | } 385 | } 386 | 387 | impl *const T { 388 | pub unsafe fn add(self, count: usize) -> Self where T: Sized 389 | } 390 | ---- 391 | 392 | [.language-rust]`<*const T>::add` [.language-rust]`unsafe` -- указатель должен быть in-bounds 393 | 394 | == ! 395 | 396 | [source,rust] 397 | ---- 398 | impl SliceIndex<[T]> for ops::Range { 399 | type Output = [T]; 400 | 401 | fn get(self, slice: &[T]) -> Option<&[T]> { 402 | if self.start > self.end || self.end > slice.len() { 403 | None 404 | } else { 405 | unsafe { Some(self.get_unchecked(slice)) } 406 | } 407 | } 408 | 409 | unsafe fn get_unchecked(self, slice: &[T]) -> &[T] { 410 | from_raw_parts( 411 | slice.as_ptr().add(self.start), 412 | self.end - self.start, 413 | ) 414 | } 415 | } 416 | ---- 417 | 418 | == split_at_mut 419 | 420 | [source,rust] 421 | ---- 422 | pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { 423 | let len = self.len(); 424 | let ptr = self.as_mut_ptr(); 425 | 426 | unsafe { 427 | assert!(mid <= len); 428 | 429 | (from_raw_parts_mut(ptr, mid), 430 | from_raw_parts_mut(ptr.add(mid), len - mid)) 431 | } 432 | } 433 | ---- 434 | 435 | `assert` необходим! 436 | 437 | == IterMut 438 | 439 | [source,rust] 440 | ---- 441 | pub struct IterMut<'a, T: 'a>(&'a mut[T]); 442 | 443 | impl<'a, T> Iterator for IterMut<'a, T> { 444 | type Item = &'a mut T; // <1> 445 | 446 | fn next(&mut self) -> Option<&'a mut T> { 447 | 448 | if self.0.is_empty() { return None; } 449 | 450 | let (l, r) = self.0.split_at_mut(1); 451 | self.0 = r; 452 | l.get_mut(0) 453 | } 454 | } 455 | ---- 456 | 457 | <1> Магия! Повторные вызовы `next` гарантируют непересекающиеся 458 | [.language-rust]`&mut` ссылки 459 | 460 | == IterMut 461 | 462 | [source,rust] 463 | ---- 464 | pub struct IterMut<'a, T: 'a>(&'a mut[T]); 465 | 466 | impl<'a, T> Iterator for IterMut<'a, T> { 467 | type Item = &'a mut T; 468 | 469 | fn next(&mut self) -> Option<&'a mut T> { 470 | 471 | if self.0.is_empty() { return None; } 472 | 473 | let (l, r) = self.0.split_at_mut(1); // <1> 474 | self.0 = r; 475 | l.get_mut(0) 476 | } 477 | } 478 | ---- 479 | 480 | <1> reborrowing, потому что не можем сделать move из поля [.language-rust]`&mut` значения :-( 481 | 482 | == swap trick 483 | 484 | [source,rust] 485 | ---- 486 | pub struct IterMut<'a, T: 'a>(&'a mut[T]); 487 | 488 | impl<'a, T> Iterator for IterMut<'a, T> { 489 | type Item = &'a mut T; 490 | 491 | fn next(&mut self) -> Option<&'a mut T> { 492 | let slice = std::mem::replace(&mut self.0, &mut []); 493 | if slice.is_empty() { return None; } 494 | 495 | let (l, r) = slice.split_at_mut(1); 496 | self.0 = r; 497 | l.get_mut(0) 498 | } 499 | } 500 | ---- 501 | 502 | [.centered] 503 | == swap trick 504 | 505 | В реальности `IterMut` устроен по другому -- как пара указателей 506 | 507 | [.centered] 508 | == ! 509 | 510 | [source,rust,subs="+quotes"] 511 | ---- 512 | struct IterMut<[.hl-error]##'a##, T> { // unused type parameter 513 | begin: *mut T, 514 | end: *mut T, 515 | } 516 | ---- 517 | 518 | [.title-slide] 519 | == Variance 520 | 521 | == Subtyping 522 | 523 | В Rust есть отношение "быть подтипом" на временах жизни: 524 | 525 | [source,rust] 526 | ---- 527 | 'a: 'b 528 | ---- 529 | 530 | Читается как [.language-rust]`'a` outlives [.language-rust]`'b` 531 | 532 | [.language-rust]`&'a T` это подтип [.language-rust]`&'b T` -- сокращение вж не 533 | нарушает memory safety 534 | 535 | == ! 536 | 537 | [source,rust] 538 | ---- 539 | fn min<'a>(xs: &Vec<'a str>, default: &'a str) -> &'a str { 540 | ... 541 | } 542 | 543 | fn main() { 544 | let xs: Vec<'static str> = vec!["hello", "world"]; 545 | let local: String = "spam".to_string() 546 | let m = min(&xs, local.as_str()) 547 | } 548 | ---- 549 | 550 | Из [.language-rust]`Vec<&'static str>` и [.language-rust]`&'a str` можно выбрать [.language-rust]`&'a str` 551 | 552 | Так как [.language-rust]`&'static str <: &'a str` + 553 | то [.language-rust]`Vec<'static str> <: Vec<'a str>` 554 | 555 | NOTE: `Vec` ковариантен по `T` 556 | 557 | == ! 558 | 559 | [source,rust] 560 | ---- 561 | include::code/evil.rs[] 562 | ---- 563 | 564 | == ! 565 | 566 | [source,rust,subs="+quotes"] 567 | ---- 568 | fn assign_ref<'a>(r: &mut &'a str, s: &'a str) { 569 | *r = s 570 | } 571 | 572 | fn evil(r: &mut &'static str) { 573 | let local: String = "spam".to_string(); 574 | let local: &str = [.hl-error]##local##.as_str(); 575 | assign_ref(r, local) 576 | } 577 | 578 | fn main() { 579 | let mut hello: &'static str = "hello"; 580 | evil(&mut hello); 581 | println!("{}", hello); 582 | } 583 | ---- 584 | 585 | NOTE: `&'a mut T` инвариантна по `T` 586 | 587 | == ! 588 | 589 | [cols="2,6",grid=none,frame=none] 590 | |=== 591 | |[.language-rust]`T`| ковариантность 592 | |[.language-rust]`&'a T`| ковариантность по [.language-rust]`'a` и `T` 593 | |[.language-rust]`&'a mut T`| ковариантность по [.language-rust]`'a`, инвариатность по `T` 594 | |[.language-rust]`*const T`| ковариантность 595 | |[.language-rust]`*mut T`| инвариантность 596 | |[.language-rust]`fn(T)`| **контр**вариантность 597 | |[.language-rust]`+fn() -> T+`| ковариантность 598 | |[.language-rust]`+fn(T) -> T+`| инвариантность 599 | |[.language-rust]`Cell<&'a T>`| инвариантность 600 | |=== 601 | 602 | [.centered] 603 | == ! 604 | 605 | Единственная разница между [.language-rust]`*const T` и [.language-rust]`*mut T` 606 | -- variance 607 | 608 | И [.language-rust]`&'a T`, и [.language-rust]`&'a mut T` ковариантны по [.language-rust]`'a` 609 | 610 | [.centered] 611 | == PhantomData 612 | 613 | [source,rust] 614 | ---- 615 | struct S<'a, T> { 616 | ... 617 | } 618 | ---- 619 | 620 | [NOTE.question] 621 | Как определить вариантность по [.language-rust]`'a` и `T`? 622 | 623 | [.invisible] 624 | Автоматически, из определения `S` 625 | 626 | [.centered] 627 | == PhantomData 628 | 629 | [source,rust] 630 | ---- 631 | struct S<'a, T> { 632 | xs: &'a mut Vec, 633 | } 634 | ---- 635 | 636 | [NOTE.question] 637 | Как определить вариантность по [.language-rust]`'a` и `T`? 638 | 639 | Автоматически, из определения `S` 640 | 641 | == PhantomData 642 | 643 | Если параметр типа не используется, то нельзя определить вариантность => все 644 | параметры должны использоваться 645 | 646 | `PhantomData`:: 647 | Магический ZST тип, который ведёт себя как `T` с точки зрения variance и dropcheck 648 | 649 | [.two-col] 650 | -- 651 | [source,rust] 652 | ---- 653 | struct A<'a, T> { 654 | value: T, 655 | r: &'a () 656 | } 657 | ---- 658 | 659 | [source,rust] 660 | ---- 661 | struct B<'a, T> { 662 | value: &'a T, 663 | 664 | } 665 | ---- 666 | -- 667 | 668 | `A` вызывает деструктор `T`, `B` нет 669 | 670 | == ! 671 | 672 | [source,rust] 673 | ---- 674 | include::code/phantom.rs[lines=1..20] 675 | ---- 676 | 677 | 678 | [.centered] 679 | == ! 680 | 681 | [source,rust] 682 | ---- 683 | include::code/phantom.rs[lines=22..] 684 | ---- 685 | 686 | == `NonNull` 687 | 688 | Для написания структур данных часто нужен ковариантный указатель, из которого 689 | удобно получать [.language-rust]`&mut T` 690 | 691 | `std::ptr::NonNull` -- как раз такой тип 692 | 693 | [source,rust] 694 | ---- 695 | size_of::>>() 696 | == size_of::>() 697 | ---- 698 | 699 | == ! 700 | 701 | [source,rust] 702 | ---- 703 | #[rustc_layout_scalar_valid_range_start(1)] 704 | pub struct NonNull { 705 | pointer: *const T, 706 | } 707 | impl NonNull { 708 | pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { 709 | NonNull { pointer: ptr as _ } 710 | } 711 | pub fn new(ptr: *mut T) -> Option { 712 | if !ptr.is_null() { 713 | Some(unsafe { Self::new_unchecked(ptr) }) 714 | } else { 715 | None 716 | } 717 | } 718 | pub const fn as_ptr(self) -> *mut T { 719 | self.pointer as *mut T 720 | } 721 | pub unsafe fn as_mut(&mut self) -> &mut T { 722 | &mut *self.as_ptr() 723 | } 724 | } 725 | ---- 726 | -------------------------------------------------------------------------------- /lectures/11-unsafe/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/11-unsafe/slides.pdf -------------------------------------------------------------------------------- /lectures/11-unsafe/unsafe-ferris.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/11-unsafe/unsafe-ferris.png -------------------------------------------------------------------------------- /lectures/12-collections/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/12-collections/slides.pdf -------------------------------------------------------------------------------- /lectures/13-macros/code/nop.rs: -------------------------------------------------------------------------------- 1 | // nop!(); // не работает 2 | macro_rules! nop { 3 | () => () 4 | } 5 | nop!(); 6 | 7 | mod m { 8 | nop!(); // Работает! 9 | } 10 | 11 | macro_rules! nop { // Переопределили макрос! 12 | () => { 92 } 13 | } 14 | -------------------------------------------------------------------------------- /lectures/13-macros/fin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/13-macros/fin.jpg -------------------------------------------------------------------------------- /lectures/13-macros/slides.adoc: -------------------------------------------------------------------------------- 1 | = Rust 2019 2 | Алексей Кладов 3 | :icons: font 4 | :lecture: Лекция 13: Макросы 5 | :table-caption!: 6 | :example-caption!: 7 | 8 | == Макросы 9 | 10 | Способ абстракции над синтаксисом языка 11 | 12 | * могущественный инструмент (свой синтаксис!) 13 | * ограниченный инструмент (только синтаксис) 14 | 15 | 16 | NOTE: Макросы не успели доделать к 1.0, текущая версия не без недостатков :-) 17 | 18 | [.title-slide] 19 | == Quick Tour 20 | 21 | == Macro By Example By Example 22 | 23 | [source,rust] 24 | ---- 25 | struct Function { ... } 26 | struct Const { ... } 27 | struct TypeAlias { ... } 28 | 29 | pub enum TraitItem { 30 | Function(Function), 31 | Const(Const), 32 | TypeAlias(TypeAlias), 33 | } 34 | ---- 35 | 36 | Poor man's OOP 37 | 38 | == Macro By Example By Example 39 | 40 | [source,rust] 41 | ---- 42 | impl From for TraitItem { 43 | fn from(item: Function) -> TraitItem { 44 | TraitItem::Function(item) 45 | } 46 | } 47 | 48 | impl From for TraitItem { 49 | ... 50 | } 51 | 52 | impl From for TraitItem { 53 | ... 54 | } 55 | 56 | ---- 57 | 58 | CAUTION: Дублирование кода! 59 | 60 | == Macro By Example By Example 61 | 62 | [source,rust] 63 | ---- 64 | macro_rules! impl_froms { 65 | ($e:ident : $($v:ident),* ) => { 66 | $( 67 | impl From<$v> for $e { 68 | fn from(it: $v) -> $e { $e::$v(it) } 69 | } 70 | )* 71 | } 72 | } 73 | 74 | pub enum TraitItem { 75 | Function(Function), 76 | Const(Const), 77 | TypeAlias(TypeAlias), 78 | } 79 | impl_froms!(TraitItem: Function, Const, TypeAlias); 80 | ---- 81 | 82 | == ! 83 | 84 | [source,rust] 85 | ---- 86 | macro_rules! impl_froms { 87 | ($e:ident : $($v:ident),* ) => { 88 | $( 89 | impl From<$v> for $e { 90 | fn from(it: $v) -> $e { $e::$v(it) } 91 | } 92 | )* 93 | } 94 | } 95 | impl_froms!(TraitItem: Function, Const, TypeAlias); 96 | ---- 97 | 98 | * `impl_froms` -- имя макроса 99 | * `+( ... ) =>+` -- паттерн 100 | * `$e:ident` -- макро переменная, матчит идентификатор 101 | * `$($v:ident),*` -- идентификаторы через `,` 102 | * `+=> { ... }+` -- результат раскрытия макроса 103 | 104 | == Token Trees 105 | 106 | Синтаксис вызова макросов: 107 | 108 | [source,rust] 109 | ---- 110 | an::path! opt_name { token_tree } 111 | ---- 112 | 113 | 114 | `token_tree` -- любая последовательность токенов Rust где `()`, `[]` и `{}` 115 | сбалансированы 116 | 117 | Результат работы макроса -- тоже token tree 118 | 119 | Можно расширять синтаксис языка! 120 | 121 | [source,rust] 122 | ---- 123 | format!( 124 | "my name is {name}, my father's name is also {name}", 125 | name = "John" 126 | ); 127 | ---- 128 | 129 | [.centered] 130 | == ! 131 | 132 | `macro_rules` это тоже макрос! 133 | 134 | [source,rust] 135 | ---- 136 | macro_rules! impl_froms { 137 | ($e:ident : $($v:ident),* ) => { 138 | $( 139 | impl From<$v> for $e { 140 | fn from(it: $v) -> $e { $e::$v(it) } 141 | } 142 | )* 143 | } 144 | } 145 | ---- 146 | 147 | Не нужно изобретать специальный синтаксис для конструкции языка 148 | 149 | == Примеры 150 | 151 | `format!`, `println!`, `log::info!` -- проверка количества аргументов без 152 | сложной системы типов 153 | 154 | `vec!` -- литерал коллекции без нового синтаксиса / vararg функций 155 | 156 | `try!` -- старая версия `?`: [.language-rust]`try!(File::create("hello.txt"))`, отложили дизайн 157 | синтаксиса 158 | 159 | 160 | NOTE: Lazy language design! 161 | 162 | == Vs. C 163 | 164 | В C макросы раскрываются препроцессором, а не компилятором 165 | 166 | [source,cpp] 167 | ---- 168 | #define squared(a) a * a 169 | 170 | int main(void) { 171 | squared(1 + 1); // 1 + 1 * 1 + 1 172 | return 0; 173 | } 174 | ---- 175 | 176 | Можно полностью поменять синтаксис языка 177 | 178 | [source,cpp] 179 | ---- 180 | #define begin { 181 | #define end } 182 | ---- 183 | 184 | == Vs. C 185 | 186 | Rust сохраняет token trees 187 | 188 | [source,rust] 189 | ---- 190 | macro_rules! squared { 191 | ($e:expr) => { $e * $e } 192 | } 193 | 194 | fn main() { 195 | squared!(1 + 1); // [1 + 1] * [1 + 1] 196 | } 197 | ---- 198 | 199 | Так как макросы синтаксически выделены (`!` + правильная скобочная 200 | последовательность), код на Rust можно парсить, не раскрывая макросов. 201 | 202 | == Vs. Scala 203 | 204 | В Rust аргумент макроса -- почти произвольная последовательность токенов 205 | 206 | В Scala -- выражение + 207 | => синтаксических возможностей меньше, но IDE легче 208 | 209 | {empty} + 210 | 211 | В Rust раскрытие макросов -- строго синтаксическое преобразование 212 | 213 | В Scala макросы могут смотреть на типы + 214 | => больше возможностей, но IDE тяжелее 215 | 216 | == Vs. Lisp 217 | 218 | Дерево токенов -- почти S-выражение 219 | 220 | **Можно** преобразовывать деревья токенов произвольным кодом 221 | 222 | Нельзя генерировать новый код во время исполнения (нет eval) 223 | 224 | [.title-slide] 225 | == Macro By Example 226 | 227 | == Видимость 228 | 229 | Совершенно другие правила видимости: макрос виден "после" объявления 230 | 231 | [source,rust] 232 | ---- 233 | include::code/nop.rs[] 234 | ---- 235 | 236 | == Видимость 237 | 238 | Макросы локальны для блоков 239 | 240 | [source,rust] 241 | ---- 242 | fn foo() -> i32 { 243 | macro_rules! bail { 244 | () => { return 92 } 245 | } 246 | 247 | if condition { 248 | bail!(); 249 | } 250 | } 251 | // тут `bail` не видно 252 | ---- 253 | 254 | == Видимость 255 | 256 | Макросы локальны для модулей без `#[macro_use]` 257 | 258 | [source,rust] 259 | ---- 260 | 261 | mod foo { 262 | macro_rules! m1 { () => (()) } 263 | } 264 | 265 | 266 | #[macro_use] 267 | mod bar { 268 | macro_rules! m2 { () => (()) } 269 | } 270 | 271 | // m2 виден, m1 нет 272 | ---- 273 | 274 | == Видимость 275 | 276 | При использовании макросов из других крейтов, обычные правила видимости 277 | 278 | .`./log/src/lib.rs` 279 | [source,rust] 280 | ---- 281 | #[macro_export] 282 | macro_rules! info { 283 | ... 284 | } 285 | ---- 286 | 287 | .`./main.rs` 288 | [source,rust] 289 | ---- 290 | fn main() { 291 | log::info!("hello, {}", 92); 292 | } 293 | ---- 294 | 295 | == Паттерны 296 | 297 | [source,rust] 298 | ---- 299 | #[macro_export] 300 | macro_rules! vec { 301 | ( $( $x:expr ),* ) => { 302 | { 303 | let mut temp_vec = Vec::new(); 304 | $( 305 | temp_vec.push($x); 306 | )* 307 | temp_vec 308 | } 309 | }; 310 | } 311 | ---- 312 | 313 | * `item`, `expr`, `ty`, `vis` ... 314 | 315 | * {blank} 316 | + 317 | |=== 318 | |`+$(...)*+` | `+$(...),*+` | `+$(...)++` | `+$(...)?+` 319 | |=== 320 | 321 | == Паттерны 322 | 323 | [source,rust] 324 | ---- 325 | macro_rules! info { 326 | (target: $target:expr, $($arg:tt)+) => ( 327 | log!(target: $target, $crate::Level::Info, $($arg)+); 328 | ); 329 | ($($arg:tt)+) => ( 330 | log!($crate::Level::Info, $($arg)+); 331 | ); 332 | } 333 | ---- 334 | 335 | * можно перечислить несколько паттернов через `;`` 336 | * вид скобочек не имеет значение 337 | * макросы могут быть рекурсивными 338 | * всё, что не `$`, сопоставляется буквально 339 | * [.language-rust]`$crate` ? 340 | 341 | == Гигиена 342 | 343 | Макросы частично гигиеничные 344 | 345 | [source,rust] 346 | ---- 347 | macro_rules! declare_x { 348 | () => { 349 | let x = 92; 350 | } 351 | } 352 | 353 | fn main() { 354 | let x = 62; 355 | declare_x!(); 356 | println!("{}", x); // 62 357 | } 358 | ---- 359 | 360 | == Гигиена 361 | 362 | Можно передать идентификатор в макрос 363 | 364 | [source,rust] 365 | ---- 366 | macro_rules! declare_var { 367 | ($var:ident) => { 368 | let $var = 92; 369 | } 370 | } 371 | 372 | fn main() { 373 | let x = 62; 374 | declare_var!(x); 375 | println!("{}", x); // 92 376 | } 377 | ---- 378 | 379 | == Гигиена 380 | 381 | Гигиена работает не везде :-( 382 | 383 | [source,rust] 384 | ---- 385 | macro_rules! declare_fn { 386 | () => { 387 | fn foo() {} 388 | } 389 | } 390 | 391 | declare_fn!(); 392 | 393 | fn main() { 394 | foo(); // Ok :-( 395 | } 396 | ---- 397 | 398 | == Грабли 399 | 400 | Гигиена + token trees решают часть проблем макросов, но не все 401 | 402 | [source,rust] 403 | ---- 404 | macro_rules! min { 405 | ($x:expr, $y:expr) => { 406 | if $x < $y { $x } else { $y } 407 | } 408 | } 409 | ---- 410 | 411 | [NOTE.question] 412 | В чём тут проблема? 413 | 414 | == Грабли 415 | 416 | [.language-rust]`$x` может быть вычислен дважды! 417 | 418 | [source,rust] 419 | ---- 420 | macro_rules! min { 421 | ($x:expr, $y:expr) => { 422 | match ($x, $y) { 423 | (x, y) => if x < y { x } else { y } 424 | } 425 | } 426 | } 427 | ---- 428 | 429 | == Грабли 430 | 431 | .my-crate 432 | [source,rust] 433 | ---- 434 | #[macro_export] 435 | macro_rules! foo { 436 | () => { 437 | use crate::X; 438 | ... 439 | } 440 | } 441 | ---- 442 | 443 | .other-crate 444 | [source,rust] 445 | ---- 446 | use my_crate::foo; 447 | 448 | fn main() { 449 | foo!(); // crate::X будет указывать на other_crate 450 | } 451 | ---- 452 | 453 | == Грабли 454 | 455 | .my-crate 456 | [source,rust] 457 | ---- 458 | #[macro_export] 459 | macro_rules! foo { 460 | () => { 461 | use $crate::X; 462 | ... 463 | } 464 | } 465 | ---- 466 | 467 | .other-crate 468 | [source,rust] 469 | ---- 470 | use my_crate::foo; 471 | 472 | fn main() { 473 | foo!(); 474 | } 475 | ---- 476 | 477 | 478 | == Встроенные Макросы 479 | 480 | * `dbg!` -- напечатать и вернут значение выражения: + 481 | [.language-rust]`dbg!(foo.bar()).baz` 482 | 483 | * `include_str!` / `include_bytes!` -- вставить ресурс в бинарь: + 484 | [.language-rust]`const LOGO: &[u8] = include_bytes!("assets/logo.png")` 485 | 486 | * `file!` / `line!` / `column!` -- имя файла, текущая строка, позиция 487 | 488 | * `stringify!` -- превращает аргумент в строку 489 | 490 | * `format_args!` -- главный `prlint`-style макрос, превращает + 491 | [.language-rust]`"foo = {}", foo` в [.language-rust]`FormatArgs<'a>` 492 | 493 | 494 | [.title-slide] 495 | == Условная Компиляция 496 | 497 | == ! 498 | 499 | [source,rust] 500 | ---- 501 | #[cfg(unix)] 502 | pub fn bytes2path(bytes: &[u8]) -> CargoResult { 503 | use std::os::unix::prelude::*; 504 | Ok(PathBuf::from(OsStr::from_bytes(bytes))) 505 | } 506 | 507 | #[cfg(windows)] 508 | pub fn bytes2path(bytes: &[u8]) -> CargoResult { 509 | use std::str; 510 | match str::from_utf8(bytes) { 511 | Ok(s) => Ok(PathBuf::from(s)), 512 | Err(..) => Err(failure::format_err!( 513 | "invalid non-unicode path" 514 | )), 515 | } 516 | } 517 | ---- 518 | 519 | Атрибут `cfg` скрывает неактивные определения. 520 | Условная компиляция -- синтаксическая, на этапе раскрытия макросов :( 521 | 522 | == Условная Компиляция 523 | 524 | .Стандартный паттерн написание unit-тестов: 525 | [source,rust] 526 | ---- 527 | #[cfg(test)] 528 | mod tests { 529 | use some_lib::test_specific_function; 530 | use super::*; 531 | 532 | #[test] 533 | fn test_foo() { ... } 534 | } 535 | ---- 536 | 537 | * `#[test]` никогда не попадают в реальный бинарь/библиотеку 538 | * `#[cfg(test)]` позволяет сгруппировать тесты и избавится от unused import 539 | 540 | 541 | == Условная Компиляция 542 | 543 | [source,rust] 544 | ---- 545 | pub fn dylib_path_envvar() -> &'static str { 546 | if cfg!(windows) { 547 | "PATH" 548 | } else if cfg!(target_os = "macos") { 549 | "DYLD_FALLBACK_LIBRARY_PATH" 550 | } else { 551 | "LD_LIBRARY_PATH" 552 | } 553 | } 554 | ---- 555 | 556 | Макрос `cfg!` можно использовать в выражениях 557 | 558 | == Условная Компиляция 559 | 560 | [source,rust] 561 | ---- 562 | fn main() { 563 | let compile_time_path = env!("PATH"); 564 | println!( 565 | "PATH at *compile* time:\n{}", 566 | compile_time_path, 567 | ); 568 | } 569 | ---- 570 | 571 | Макросы `env!` и `option_env!` позволяют смотреть на переменные окружения в compile time 572 | 573 | [.title-slide] 574 | == Процедурные Макросы 575 | 576 | == ! 577 | 578 | Макросами могут быть обычные функции: 579 | 580 | [source,rust] 581 | ---- 582 | pub enum TokenTree { 583 | Group(Group), 584 | Ident(Ident), 585 | Punct(Punct), 586 | Literal(Literal), 587 | } 588 | 589 | impl Group { 590 | pub fn delimiter(&self) -> Delimiter 591 | pub fn stream(&self) -> TokenStream 592 | } 593 | 594 | type TokenStream = Iterator; // 👋 595 | 596 | ---- 597 | 598 | [source,rust] 599 | ---- 600 | #[proc_macro] 601 | pub fn squared(arg: TokenStream) -> TokenStream { 602 | format!("({arg}) * ({arg})", arg = arg).parse().unwrap() 603 | } 604 | ---- 605 | 606 | == Процедурные Макросы 607 | 608 | * нет гигиены 609 | * можно использовать только на уровне модуля (пример со `squared` не работает) 610 | * нужно опредеять в отдельном крейте с 611 | + 612 | [source,toml] 613 | ---- 614 | [lib] 615 | proc-macro = true 616 | ---- 617 | 618 | == Derive 619 | 620 | [source,rust] 621 | ---- 622 | #[derive(Clone, Copy, PartialEq, Eq)] 623 | struct Vec3([f3; 3]); 624 | ---- 625 | 626 | `derive` это тоже макрос, `derive(Clone)` работает синтаксически 627 | 628 | Можно писать свои `derive`! 629 | 630 | [source,rust] 631 | ---- 632 | #[proc_macro_derive(MyTrait)] 633 | pub fn derive_my_trait(input: TokenStream) -> TokenStream { 634 | ... 635 | } 636 | ---- 637 | 638 | == Derive 639 | 640 | Для `Derive`, нужно распарсить дерево токенов как определение ADT 641 | 642 | Крейт `syn` позволяет парсить деревья токенов в AST Rust 643 | 644 | `syn` -- обычный код, никак не связанный с компилятором 645 | 646 | == Пример 647 | 648 | Хотя интерфейс процедурных макросов простой, API syn очень большое! 649 | 650 | [source,rust] 651 | ---- 652 | #[derive(HeapSize)] 653 | struct Demo<'a, T: ?Sized> { 654 | a: Box, 655 | b: u8, 656 | c: &'a str, 657 | d: String, 658 | } 659 | ---- 660 | 661 | `HeapSize` -- размер объекта, учитывая данные в куче 662 | 663 | "Глубокий" `std::size_of` 664 | 665 | == ! 666 | 667 | [source,rust] 668 | ---- 669 | #[proc_macro_derive(HeapSize)] 670 | pub fn derive_heap_size(input: TokenStream) -> TokenStream { 671 | let input = parse_macro_input!(input as DeriveInput); 672 | let name = input.ident; 673 | let generics = add_trait_bounds(input.generics); 674 | let (impl_generics, ty_generics, where_clause) = 675 | generics.split_for_impl(); 676 | let sum = heap_size_sum(&input.data); 677 | let expanded = quote! { 678 | impl #impl_generics heapsize::HeapSize 679 | for #name #ty_generics 680 | #where_clause 681 | { 682 | fn heap_size_of_children(&self) -> usize { 683 | #sum 684 | } 685 | } 686 | }; 687 | proc_macro::TokenStream::from(expanded) 688 | } 689 | ---- 690 | 691 | == ! 692 | 693 | [source,rust] 694 | ---- 695 | fn heap_size_sum(data: &Data) -> TokenStream { 696 | match *data { 697 | Data::Struct(ref data) => { 698 | match data.fields { 699 | Fields::Unnamed(ref fields) => { 700 | let recurse = fields.unnamed.iter() 701 | .enumerate().map(|(i, f)| { 702 | let index = Index::from(i); 703 | quote! { 704 | HeapSize::heap_size_of_children( 705 | &self.#index 706 | ) 707 | } 708 | }); 709 | quote! { 0 #(+ #recurse)* } 710 | } 711 | Fields::Named(_) | Fields::Unit => panic!("TODO") 712 | } 713 | } 714 | Data::Enum(_) | Data::Union(_) => panic!("TODO") 715 | } 716 | } 717 | ---- 718 | 719 | == serde 720 | 721 | [.text-center.lead] 722 | https://serde.rs/ 723 | 724 | serde:: 725 | Фреймворк для сериализации и десериализации на Rust 726 | 727 | TIP: Один из самый замечательных крейтов! 728 | 729 | Трейты + Макросы = гибкая и быстрая сериализация вне языка 730 | 731 | == serde 732 | 733 | Ядро дизайна -- статически диспетчеризуемый visitor 734 | 735 | [source,rust] 736 | ---- 737 | #[derive(Serialize)] 738 | struct Rgb { 739 | r: u8, 740 | g: u8, 741 | b: u8, 742 | } 743 | ---- 744 | 745 | == ! 746 | 747 | [source,rust] 748 | ---- 749 | struct Rgb { 750 | r: u8, 751 | g: u8, 752 | b: u8, 753 | } 754 | 755 | impl Serialize for Rgb { 756 | fn serialize(&self, serializer: S) -> Result 757 | where 758 | S: Serializer, 759 | { 760 | let mut rgb = serializer.serialize_struct("Rgb", 3)?; 761 | rgb.serialize_field("r", &self.r)?; 762 | rgb.serialize_field("g", &self.g)?; 763 | rgb.serialize_field("b", &self.b)?; 764 | rgb.end() 765 | } 766 | } 767 | ---- 768 | 769 | Статический reflection! 770 | 771 | == Serialize 772 | 773 | [source,rust] 774 | ---- 775 | pub trait Serialize { 776 | fn serialize(&self, serializer: S) -> Result 777 | where 778 | S: Serializer; 779 | } 780 | ---- 781 | 782 | S -- формат данных (JSON, YAML, XML) 783 | 784 | S -- параметр типа, после монофорфизации получаем код, который напрямую создаёт 785 | JSON 786 | 787 | == Serializer 788 | 789 | [source,rust] 790 | ---- 791 | pub trait Serializer: Sized { 792 | type Ok; 793 | type Error: Error; 794 | 795 | type SerializeStruct 796 | : SerializeStruct; 797 | 798 | fn serialize_i8(self, v: i8) 799 | -> Result; 800 | 801 | fn serialize_i16(self, v: i16) 802 | -> Result; 803 | 804 | fn serialize_struct( 805 | self, 806 | name: &'static str, 807 | len: usize, 808 | ) -> Result; 809 | } 810 | ---- 811 | 812 | == ! 813 | 814 | image::./fin.jpg[width=100%] 815 | -------------------------------------------------------------------------------- /lectures/13-macros/slides.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/13-macros/slides.pdf -------------------------------------------------------------------------------- /lectures/fin.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matklad/rust-course/d9d27de649af25d2c3d66aec41ccf517149d9e9c/lectures/fin.jpg -------------------------------------------------------------------------------- /lectures/rust-logo-blk.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lectures/slides.css: -------------------------------------------------------------------------------- 1 | @page { 2 | size: 1024px 768px; 3 | margin: 0; 4 | } 5 | body { 6 | display: flex; 7 | flex-direction: column; 8 | } 9 | html { 10 | font-size: 22pt; 11 | } 12 | 13 | img { 14 | display: block; 15 | margin: 0 auto; 16 | } 17 | .admonitionblock.question td.icon .icon-note:before { 18 | content: "\f128"; 19 | color: #19407c; 20 | } 21 | .admonitionblock > table td.content code { 22 | font-weight: bold; 23 | } 24 | 25 | .slide { 26 | position: relative; 27 | width: 1024px; 28 | height: 768px; 29 | padding: 10px; 30 | align-self: center; 31 | display: flex; 32 | flex-direction: column; 33 | } 34 | /* 35 | 36 | // When enabled, this causes inexplicable artifacts in PDF generation 37 | @media not print { 38 | } 39 | */ 40 | 41 | .title-slide { 42 | justify-content: center; 43 | } 44 | .slide-content { 45 | height: 100%; 46 | } 47 | 48 | .title-slide h1, .title-slide h2, .title-slide h3 { 49 | text-align: center; 50 | margin-top: 0; 51 | } 52 | 53 | h1, h2, h3 { 54 | margin-top: 0em; 55 | } 56 | h1 { 57 | font-size: 3em; 58 | } 59 | 60 | h2 { 61 | font-size: 2.125em; 62 | } 63 | h3 { 64 | font-size: 1.6875em; 65 | letter-spacing: -.01em; 66 | } 67 | 68 | h1 > code, h2 > code, h3 > code { 69 | color: #ba3925; 70 | } 71 | .hl-kw { 72 | font-weight: bold; 73 | color: #000080; 74 | } 75 | .hl-lifetime { 76 | color: #20999D; 77 | } 78 | .hl-macro { 79 | color: #DD6718; 80 | } 81 | .hl-number { 82 | color: #0000ff; 83 | } 84 | .hl-string { 85 | font-weight: bold; 86 | color: #008000; 87 | } 88 | .hl-prompt { 89 | font-weight: bold; 90 | color: #7a2518; 91 | } 92 | .hl-comment { 93 | color: rgba(0, 0, 0, .6); 94 | } 95 | .hl-error { 96 | text-decoration: red wavy underline; 97 | text-decoration-skip-ink: none; 98 | } 99 | .no-title > h3 { 100 | display: none; 101 | } 102 | .slide > footer { 103 | position: absolute; 104 | right: 10px; 105 | bottom: 10px; 106 | font-family: "Fira Mono"; 107 | } 108 | .slide > footer > p { 109 | margin-block-start: 0; 110 | margin-block-end: 0; 111 | } 112 | 113 | .two-col > .slide-content { 114 | display: flex; 115 | flex-direction: row; 116 | } 117 | .two-col > .slide-content > *:first-child { 118 | margin-right: 10px; 119 | flex: 50%; 120 | } 121 | .two-col > .slide-content > *:last-child { 122 | margin-left: 10px; 123 | flex: 50%; 124 | } 125 | .two-col > .content { 126 | display: flex; 127 | flex-direction: row; 128 | } 129 | .two-col > .content > *:first-child { 130 | margin-right: 10px; 131 | flex: 50%; 132 | } 133 | .two-col > .content > *:last-child { 134 | margin-left: 10px; 135 | flex: 50%; 136 | } 137 | 138 | .centered > .slide-content { 139 | position: absolute; 140 | width: 1004px; 141 | height: 748px; 142 | display: flex; 143 | flex-direction: column; 144 | justify-content: center; 145 | } 146 | 147 | .exampleblock > .content > .listingblock > .content > .highlight { 148 | margin-top: 0; 149 | } 150 | .sidebarblock { 151 | border-width: 3px; 152 | background-color: white; 153 | } 154 | 155 | .invisible { 156 | visibility: hidden 157 | } 158 | 159 | .fragment { 160 | visibility: hidden 161 | } 162 | 163 | .slide-fragment .fragment { 164 | visibility: visible; 165 | } 166 | 167 | .listingblock > .title, .ulist > .title { 168 | font-size: 1.0625rem; 169 | letter-spacing: -.01em; 170 | color: rgba(0, 0, 0, .8); 171 | font-style: normal 172 | } 173 | -------------------------------------------------------------------------------- /lectures/template.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | document: (node) => { 3 | const fullTitle = node.getAttribute('lecture') 4 | const title = fullTitle.substring(0, fullTitle.indexOf(':')) 5 | const subtitle = fullTitle.substring(fullTitle.indexOf(':') + 1) 6 | 7 | return ` 8 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | 17 | 18 | 19 | ${node.getDocumentTitle()} 20 | compscicenter.ru 21 | ${node.getDocument().getAttribute('email')} 22 | 23 | 24 | 25 | ${title}${subtitle} 26 | 27 | ${node.getContent()} 28 | 32 | ` 33 | }, 34 | 35 | section: (node) => node.getRoles().includes("title-slide") ? 36 | ` 37 | 38 | ${node.getTitle()} 39 | ` 40 | : ` 41 | 42 | ${node.getTitle()} 43 | 44 | ${node.getContent()} 45 | 46 | 49 | `, 50 | 51 | paragraph: (node) => `${node.getContent()}`, 52 | 53 | image: (node) => { 54 | const width = node.getAttribute('width') 55 | const height = node.getAttribute('height') 56 | return `` 61 | } 62 | // listing: (node) => node.getContent(), 63 | } 64 | 65 | function init() { 66 | function firstToken(TOKENS, text) { 67 | let tag = null; 68 | let len = undefined; 69 | let index = text.length; 70 | for (let [t, re] of Object.entries(TOKENS)) { 71 | const m = text.match(re) 72 | if (!m) continue 73 | if (m.index < index) { 74 | index = m.index; 75 | tag = t; 76 | len = m[0].length; 77 | } 78 | } 79 | if (index == 0) { 80 | return [tag, Math.max(len, 1)] 81 | } else { 82 | return ['text', Math.max(index, 1)] 83 | } 84 | } 85 | 86 | function tokenize(TOKENS, text) { 87 | const res = []; 88 | while (true) { 89 | if (text.length == 0) break; 90 | let [tag, len] = firstToken(TOKENS, text); 91 | let tok = text.substring(0, len); 92 | res.push([tag, tok]) 93 | text = text.substring(len); 94 | } 95 | return res 96 | } 97 | 98 | const RUST_TOKENS = { 99 | number: /\b([0-9][0-9_]*(\.[0-9]+)?|0[xob][_0-9a-zA-Z]+)([iuf](8|16|32|64|128|size))?\b/, 100 | string: /"[^"\n]*"|b?'.'/, 101 | lifetime: /'\w+(?!')/, 102 | kw: /\b(union|static|move|super|type|trait|where|self|impl|true|false|extern|crate|fn|match|in|if|else|while|for|loop|pub|let|return|break|continue|mut|const|ref|struct|enum|use|mod|as|unsafe|dyn)\b/, 103 | macro: /[a-zA-Z_]+!(?=[(\\[{])/, 104 | comment: /\/\/.*/, 105 | prompt: /\$/, 106 | text: /<\w+[^<]*>|<\/\w+>/, 107 | }; 108 | 109 | const LANG_TOKENS = { 110 | number: /\b[0-9][0-9_]*(\.[0-9]+)?(d)?\b/, 111 | string: /(?)/, 112 | kw: /\b(template|typedef|typename|void|in|if|else|while|for|let|return|break|continue|const|struct|enum|use|as|def|private|static)\b/, 113 | comment: /\/\/.*/, 114 | macro: /#\w+ .*/, 115 | text: /<\w+[^<]*>|<\/\w+>/, 116 | } 117 | 118 | const SH_TOKENS = { 119 | comment: /#.*/, 120 | prompt: /\$/, 121 | text: /<\w+[^<]*>|<\/\w+>/, 122 | } 123 | 124 | const langs = { 125 | 'rust': RUST_TOKENS, 126 | 'java': LANG_TOKENS, 127 | 'scala': LANG_TOKENS, 128 | 'cpp': LANG_TOKENS, 129 | 'sh': SH_TOKENS, 130 | } 131 | 132 | for (const [lang, TOKENS] of Object.entries(langs)) { 133 | for (const elt of document.getElementsByClassName(`language-${lang}`)) { 134 | let newText = ""; 135 | for (let [tag, text] of tokenize(TOKENS, elt.innerHTML)) { 136 | if (tag == 'text') { 137 | newText += text; 138 | continue; 139 | } 140 | newText += `${text}` 141 | } 142 | elt.innerHTML = newText 143 | } 144 | } 145 | 146 | function currentSlide() { 147 | const loc = location.hash.replace("#slide-", ""); 148 | if (!loc) return 0; 149 | return parseInt(loc); 150 | } 151 | 152 | const nSlides = document.getElementsByClassName('slide').length - 2; 153 | 154 | function go(delta) { 155 | let slide = currentSlide() + delta; 156 | slide = Math.max(slide, 0); 157 | slide = Math.min(slide, nSlides - 1); 158 | location.hash = "#slide-" + slide; 159 | } 160 | 161 | document.onkeydown = function (e) { 162 | switch (e.keyCode) { 163 | case 37: 164 | go(-1); 165 | break; 166 | case 33: 167 | go(-10); 168 | e.preventDefault() 169 | break; 170 | case 39: 171 | go(+1); 172 | break; 173 | case 32: // space 174 | e.preventDefault() 175 | go(+1); 176 | break; 177 | case 34: 178 | go(+10); 179 | e.preventDefault() 180 | break; 181 | } 182 | }; 183 | } 184 | 185 | function insertScreenStyle() { 186 | if (!navigator.userAgent.includes("HeadlessChrome")) { 187 | const style = document.createElement('style') 188 | style.innerHTML = ` 189 | body { 190 | padding-bottom: 2400px; 191 | } 192 | .slide { 193 | margin-top: 1em; 194 | border: 1px solid #ba3925; 195 | padding: 9px; 196 | } 197 | ` 198 | const ref = document.querySelector('script') 199 | ref.parentNode.insertBefore(style, ref) 200 | } 201 | } 202 | 203 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "dependencies": { 4 | "asciidoctor-pdf.js": "file:./asciidoctor-pdf.js" 5 | }, 6 | "scripts": { 7 | "adoc": "./node_modules/asciidoctor-pdf.js/bin/asciidoctorjs-pdf --template-require ./lectures/template.js" 8 | } 9 | } 10 | --------------------------------------------------------------------------------
${node.getContent()}