{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Google Colab Rust Setup\n", "\n", "The following cell is used to set up and spin up a Jupyter Notebook environment with a Rust kernel using Nix and IPC Proxy. " ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!wget -qO- https://gist.github.com/wiseaidev/2af6bef753d48565d11bcd478728c979/archive/3f6df40db09f3517ade41997b541b81f0976c12e.tar.gz | tar xvz --strip-components=1\n", "!bash setup_evcxr_kernel.sh" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "---" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Tour of the EvCxR Jupyter Kernel\n", "For those not already familiar with Jupyter notebook, it lets you write code into \"cells\" like the box below. Cells can alternatively contain markdown, like this text here. Each code cell is compiled and executed separately, but variables, defined functions etc persist between cells.\n", "\n", "## Printing to outputs and evaluating expressions\n", "Lets print something to stdout and stderr then return a final expression to see how that's presented. Note that stdout and stderr are separate streams, so may not appear in the same order is their respective print statements." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Hello error\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Hello world\n" ] }, { "data": { "text/plain": [ "\"Hello world\"" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "println!(\"Hello world\");\n", "eprintln!(\"Hello error\");\n", "format!(\"Hello {}\", \"world\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Assigning and making use of variables\n", "We define a variable `message`, then in the subsequent cell, modify the string and finally print it out. We could also do all this in the one cell if we wanted." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "let mut message = \"Hello \".to_owned();" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "message.push_str(\"world!\");" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"Hello world!\"" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "message" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Defining and redefining functions\n", "Next we'll define a function" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "pub fn fib(x: i32) -> i32 {\n", " if x <= 2 {0} else {fib(x - 2) + fib(x - 1)}\n", "}" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(1..13).map(fib).collect::>()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Hmm, that doesn't look right. Lets redefine the function. In practice, we'd go back and edit the function above and reevalute it, but here, lets redefine it in a separate cell." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "pub fn fib(x: i32) -> i32 {\n", " if x <= 2 {1} else {fib(x - 2) + fib(x - 1)}\n", "}" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144]" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let values = (1..13).map(fib).collect::>();\n", "values" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Spawning a separate thread and communicating with it\n", "We can spawn a thread to do stuff in the background, then continue executing code in other cells." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "use std::sync::{Mutex, Arc};\n", "let counter = Arc::new(Mutex::new(0i32));\n", "std::thread::spawn({\n", " let counter = Arc::clone(&counter);\n", " move || {\n", " for i in 1..300 {\n", " *counter.lock().unwrap() += 1;\n", " std::thread::sleep(std::time::Duration::from_millis(100));\n", " }\n", "}});" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "65" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "*counter.lock()?" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "120" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "*counter.lock()?" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loading external crates\n", "We can load external crates. This one takes a while to compile, but once it's compiled, subsequent cells shouldn't need to recompile it, so it should be much quicker." ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "\"AQIDBA==\"" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ ":dep base64 = \"0.10.1\"\n", "base64::encode(&vec![1, 2, 3, 4])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Customizing how types are displayed\n", "We can also customize how our types are displayed, including presenting them as HTML. Here's an example where we define a custom display function for a type `Matrix`." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "use std::fmt::Debug;\n", "pub struct Matrix {pub values: Vec, pub row_size: usize}\n", "impl Matrix {\n", " pub fn evcxr_display(&self) {\n", " let mut html = String::new();\n", " html.push_str(\"\");\n", " for r in 0..(self.values.len() / self.row_size) {\n", " html.push_str(\"\");\n", " for c in 0..self.row_size {\n", " html.push_str(\"\");\n", " }\n", " html.push_str(\"\"); \n", " }\n", " html.push_str(\"
\");\n", " html.push_str(&format!(\"{:?}\", self.values[r * self.row_size + c]));\n", " html.push_str(\"
\");\n", " println!(\"EVCXR_BEGIN_CONTENT text/html\\n{}\\nEVCXR_END_CONTENT\", html);\n", " }\n", "}" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
123
456
789
" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "let m = Matrix {values: vec![1,2,3,4,5,6,7,8,9], row_size: 3};\n", "m" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also return images using add-on crates like `evcxr_image`, which adds support for displaying RGB and grayscale images in Evcxr. Note, the version of the `image` crate used must match the version used by `evcxr_image`, otherwise the types will effectively be different and the image won't get displayed." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "image/png": "iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAIAAADTED8xAAAGBklEQVR4nO3TCVIcRxBAUfn+h7aRRUgIhpleasnlvQiHBczSVZn/nx8//v3x7u3f0Mvb0v8O4M3bj9DI28Z/DODN22+gi1/rrgGa+r3rGqCjj4uuAdr5tOUaoJevK64BGnm43xqgi++WWwO08GSzNUB9z9daAxT3cqc1QGVHFloDlHVwmzVATcdXWQMUdGqPNUA1Z5dYA5RyYYM1QB3X1lcDFHF5dzVABXcWVwOkd3NrNUBu91dWAyQ2ZF81QFajllUDpDRwUzVAPmPXVAMkM3xHNUAmMxZUA6QxaTs1QA7zVlMDJDB1LzVAdLOXUgOEtmAjNUBca9ZRAwS1bBc1QEQrF1EDhLN4CzVALOtXUAMEsmX/NEAUu5ZPA4SwcfM0wH57104DbLZ95zTAThEWTgNsE2TbNMAecVZNA2wQas80wGrRlkwDLBVwwzTAOjHXSwMsEna3NMAKkRdLA0wXfKs0wFzxV0oDTJRinzTALFmWSQNMkWiTNMB4udZIAwyWboc0wEgZF0gDDJN0ezTAGHlXRwMMkHpvNMBd2ZdGA9xSYGM0wHU11kUDXFRmVzTAFZUWRQOcVmxLNMA59VZEA5xQcj80wFFVl0MDHFJ4MzTAa7XXQgO8UH4nNMAzHRZCA3yryTZogMf6rIIGeKDVHmiAz7otgQb4S8MN0AB/9By/BnjXdvYa4KfOg9cA3aeuge6MXAOtmfcbDfRl2L9ooCmT/k0DHRnzRxpox4w/0UAvBvyVBhox3Yc00IXRfkcDLZjrExqoz1Cf00BxJvqSBiozziM0UJZZHqSBmgzyOA0UZIqnaKAaIzxLA6WY3wUaqMPwrtFAESZ3mQYqMLY7NJCemd2kgdwM7D4NJGZaQ2ggK6MaRQMpmdNAGsjHkMbSQDImNJwGMjGeGTSQhtlMooEcDGYeDSRgKlNpIDojmU0DoZnHAhqIyzDW0EBQJrGMBiIyhpU0EI4ZLKaBWAxgPQ0E4va30EAUrn4XDYTg3jfSwH4ufS8NbObGt9PATq47Ag1s466D0MAeLjoODWzglkPRwGquOBoNLOV+A9LAOi43Jg0s4mbD0sAKrjUyDUznToPTwFwuND4NTOQ2U9DALK4yCw1M4R4T0cB4LjEXDQzmBtPRwEiuLyMNDOPuktLAGC4uLw0M4NZS08Bdriw7DdzivgrQwHUuqwYNXOSmytDAFa6pEg2c5o6K0cA5LqgeDZzgdkrSwFGupioNHOJeCtPAay6lNg284EbK08AzrqMDDXzLXTShgcdcRB8aeMAttKKBz1xBNxr4S/fzt6SBP1ofvjENvOt78vY08FPTY/M/DbQ8Mx90b6DdgfmidQO9Tss3+jbQ6Kg81bSBLufkgI4NtDgkh7VroP4JOalXA8WPxyWNGqh8Nm7o0kDZg3FbiwZqnopB6jdQ8EgMVbyBaudhgsoNlDoM05RtoM5JmKxmA0WOwRIFG6hwBhaq1kD6A7BcqQZyPz2b1Gkg8aOzVZEGsj43AVRoIOVDE0b6BvI9McHkbiDZ4xJS4gYyPSuBZW0gzYMSXsoGcjwlSeRrIMEjkkqyBqI/HwllaiD0w5FWmgbiPhnJ5Wgg6GNRQoIGIj4ThURvINwDUU7oBmI9DUXFbSDQo1Ba0AaiPAcNRGwgxEPQRrgG9j8BzcRqYPPX01KgBnZ+N41FaWDbF9NeiAb2fCv8b38DG74SPtjcwOrvgy92NrD0y+Ab2xpY903w1J4GFn0NHLChgRXfAYetbmD6F8BJSxuY++lwyboGJn403LCogVmfC7etaGDKh8Ig0xsY/4kw1NwGBn8cTDCxgZGfBdPMamDYB8FkUxoY8ymwxPgGBnwELDS4gbvvh+VGNnDrzbDJsAauvxO2GtPAxbdBAAMauPIeCONuA6ffAMHcauDcqyGk6w2ceCkEdrGBo6+D8K40cOhFkMTpBl6/AlI518CLP0NCJxp49jdI62gD3/4BkjvUwOPfQgmvG3jwKyjkRQOff4ZynjXw1w9Q1LcN/PkXlPa4gff/QQMPGvj5H7TxuQEB0M1fDQiAhv40IAB6em/gP4B1AQ2EIi+6AAAAAElFTkSuQmCC" }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ ":dep image = \"0.23\"\n", ":dep evcxr_image = \"1.1\"\n", "use evcxr_image::ImageDisplay;\n", "\n", "image::ImageBuffer::from_fn(256, 256, |x, y| {\n", " if (x as i32 - y as i32).abs() < 3 {\n", " image::Rgb([0, 0, 255])\n", " } else {\n", " image::Rgb([0, 0, 0])\n", " }\n", "})" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Display of compilation errors\n", "Here's how compilation errors are presented. Here we forgot an & and passed a String instead of an &str." ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "ename": "Error", "evalue": "mismatched types", "output_type": "error", "traceback": [ "s.push_str(format!(\"foo {}\", 42));", "\u001b[91m ^^^^^^^^^^^^^^^^^^^^^\u001b[0m \u001b[94mexpected &str, found struct `std::string::String`\u001b[0m", "mismatched types" ] } ], "source": [ "let mut s = String::new();\n", "s.push_str(format!(\"foo {}\", 42));" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Async await\n", "Async functions can be called and the results awaited. Currently this uses Tokio as the executor. The first run of code that uses await will likely be slow while Tokio is compiled. We explicitly add tokio as a dependency so that we can turn on the \"full\" feature. This is needed for TcpStream. This example also demostrates use of the question mark operator, which upon finding that the result contained an error, prints it to stderr." ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ ":dep tokio = {version = \"0.2\", features = [\"full\"]}" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "invalid port value\n" ] } ], "source": [ "let mut stream : tokio::net::TcpStream = tokio::net::TcpStream::connect(\"127.0.0.1:99999\").await?;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Note, we needed to give an explicit type to the stream variable, because rustc, at least at the time of writing suggests `tokio::net::tcp::TcpStream`, which is private. We need to explicitly provide the public alias in such cases.\n", "\n", "Now let's try again with a valid port number. First, make something listen on port 6543. You might be able to use netcat, e.g. `nc -t -l 6543`." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "let mut stream : tokio::net::TcpStream = tokio::net::TcpStream::connect(\"127.0.0.1:6543\").await?;" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "use tokio::io::AsyncWriteExt;\n", "stream.write(b\"Hello, world!\\n\").await?;" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "At this point, netcat, or whatever was listening on port 6543 should have received (and printed) \"Hello, world!\"." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Seeing what variables have been defined\n", "We can print a table of defined variables and their types with the :vars command." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
VariableType
counterstd::sync::Arc<std::sync::Mutex<i32>>
messageString
muser_code_13::Matrix<i32>
valuesstd::vec::Vec<i32>
" ], "text/plain": [ "counter: std::sync::Arc>\n", "message: String\n", "m: user_code_13::Matrix\n", "values: std::vec::Vec\n" ] }, "execution_count": 19, "metadata": {}, "output_type": "execute_result" } ], "source": [ ":vars" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Other built-in commands can be found via :help" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "data": { "text/plain": [ ":vars List bound variables and their types\n", ":opt [level] Toggle/set optimization level\n", ":fmt [format] Set output formatter (default: {:?}). \n", ":explain Print explanation of last error\n", ":clear Clear all state, keeping compilation cache\n", ":dep Add dependency. e.g. :dep regex = \"1.0\"\n", ":sccache [0|1] Set whether to use sccache.\n", ":linker [linker] Set/print linker. Supported: system, lld\n", ":version Print Evcxr version\n", ":preserve_vars_on_panic [0|1] Try to keep vars on panic\n", "\n", "Mostly for development / debugging purposes:\n", ":last_compile_dir Print the directory in which we last compiled\n", ":timing Toggle printing of how long evaluations take\n", ":last_error_json Print the last compilation error as JSON (for debugging)\n", ":time_passes Toggle printing of rustc pass times (requires nightly)\n", ":internal_debug Toggle various internal debugging code\n" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ ":help" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Rust", "language": "rust", "name": "rust" }, "language_info": { "codemirror_mode": "rust", "file_extension": ".rs", "mimetype": "text/rust", "name": "Rust", "pygment_lexer": "rust", "version": "" } }, "nbformat": 4, "nbformat_minor": 4 }