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.
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.
In [2]:
println!("Hello world");
eprintln!("Hello error");
format!("Hello {}", "world")
Out[2]:
In [3]:
let mut message = "Hello ".to_owned();
In [4]:
message.push_str("world!");
In [5]:
message
Out[5]:
In [6]:
pub fn fib(x: i32) -> i32 {
if x <= 2 {0} else {fib(x - 2) + fib(x - 1)}
}
In [7]:
(1..13).map(fib).collect::<Vec<i32>>()
Out[7]:
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.
In [8]:
pub fn fib(x: i32) -> i32 {
if x <= 2 {2} else {fib(x - 2) + fib(x - 1)}
}
In [9]:
let values = (1..13).map(fib).collect::<Vec<i32>>();
values
Out[9]:
In [2]:
use std::sync::{Mutex, Arc};
let counter = Arc::new(Mutex::new(0i32));
std::thread::spawn({
let counter = Arc::clone(&counter);
move || {
for i in 1..300 {
*counter.lock().unwrap() += 1;
std::thread::sleep(std::time::Duration::from_millis(100));
}
}});
In [3]:
*counter.lock()?
Out[3]:
In [4]:
*counter.lock()?
Out[4]:
In [14]:
:dep base64 = "0.10.1"
base64::encode(&vec![1, 2, 3, 4])
Out[14]:
In [2]:
use std::fmt::Debug;
pub struct Matrix<T> {pub values: Vec<T>, pub row_size: usize}
impl<T: Debug> Matrix<T> {
pub fn evcxr_display(&self) {
let mut html = String::new();
html.push_str("<table>");
for r in 0..(self.values.len() / self.row_size) {
html.push_str("<tr>");
for c in 0..self.row_size {
html.push_str("<td>");
html.push_str(&format!("{:?}", self.values[r * self.row_size + c]));
html.push_str("</td>");
}
html.push_str("</tr>");
}
html.push_str("</table>");
println!("EVCXR_BEGIN_CONTENT text/html\n{}\nEVCXR_END_CONTENT", html);
}
}
In [3]:
let m = Matrix {values: vec![1,2,3,4,5,6,7,8,9], row_size: 3};
m
Out[3]:
We can also return images, we just need to base64 encode them. First, we set up code for displaying RGB and grayscale images.
In [2]:
:dep image = "0.23"
In [4]:
extern crate image;
extern crate base64;
pub trait EvcxrResult {fn evcxr_display(&self);}
impl EvcxrResult for image::RgbImage {
fn evcxr_display(&self) {
let mut buffer = Vec::new();
image::png::PNGEncoder::new(&mut buffer).encode(&**self, self.width(), self.height(),
image::ColorType::Rgb8).unwrap();
let img = base64::encode(&buffer);
println!("EVCXR_BEGIN_CONTENT image/png\n{}\nEVCXR_END_CONTENT", img);
}
}
impl EvcxrResult for image::GrayImage {
fn evcxr_display(&self) {
let mut buffer = Vec::new();
image::png::PNGEncoder::new(&mut buffer).encode(&**self, self.width(), self.height(),
image::ColorType::L8).unwrap();
let img = base64::encode(&buffer);
println!("EVCXR_BEGIN_CONTENT image/png\n{}\nEVCXR_END_CONTENT", img);
}
}
In [5]:
image::ImageBuffer::from_fn(256, 256, |x, y| {
if (x as i32 - y as i32).abs() < 3 {
image::Rgb([0, 0, 255])
} else {
image::Rgb([0, 0, 0])
}
})
Out[5]:
In [17]:
let mut s = String::new();
s.push_str(format!("foo {}", 42));
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.
In [2]:
:dep tokio = {version = "0.2", features = ["full"]}
In [3]:
let mut stream : tokio::net::TcpStream = tokio::net::TcpStream::connect("127.0.0.1:99999").await?;
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.
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.
In [4]:
let mut stream : tokio::net::TcpStream = tokio::net::TcpStream::connect("127.0.0.1:6543").await?;
In [5]:
use tokio::io::AsyncWriteExt;
stream.write(b"Hello, world!\n").await?;
At this point, netcat, or whatever was listening on port 6543 should have received (and printed) "Hello, world!".
In [19]:
:vars
Out[19]:
Other built-in commands can be found via :help
In [6]:
:help
Out[6]:
In [ ]: