What's the correct way to implement the equivalent of multiple mutable (statically allocated, statically dispatched, etc.) callbacks in Rust? - callback

I have the following example code, which is the standard basis of event-driven APIs in other programming languages, but in Rust the borrow checker blocks it with "cannot borrow p1 as mutable more than once at a time":
struct Pen {
color_cmyk: u32,
ink: usize,
}
impl Pen {
pub fn new() -> Pen {
Pen {
color_cmyk: 0x80800000,
ink: 20000,
}
}
pub fn write(&mut self, text: &str) -> bool {
if self.ink < text.len() {
return false;
}
self.ink -= text.len();
true
}
}
fn main() {
println!("Hello, world !");
let mut p1 = Pen::new();
p1.write("Hello");
println!("ink: {}, color: {}", p1.ink, p1.color_cmyk);
let mut cb = |text| if p1.write(text) {
println!("{}", text);
} else {
println!("Out of ink !");
};
let mut cb2 = |text| {
p1.write(text);
p1.ink
};
cb("Hello");
cb("World");
println!("{}", cb2("Hello"));
}
error[E0499]: cannot borrow `p1` as mutable more than once at a time
--> src/main.rs:37:23
|
31 | let mut cb = |text| if p1.write(text) {
| ------ -- previous borrow occurs due to use of `p1` in closure
| |
| first mutable borrow occurs here
...
37 | let mut cb2 = |text| {
| ^^^^^^ second mutable borrow occurs here
38 | p1.write(text);
| -- borrow occurs due to use of `p1` in closure
...
45 | }
| - first borrow ends here
The code can be used, for example, to implement two callbacks to a window: one for handling keyboard events and another for handling mouse events, both of which update the window state (ex: changing color, closing the window, etc.).
I know that this question appears elsewhere in Stack Overflow and other forums, but in general, the answers focus on describing the reason of the problem and rarely propose a complete general solution for it:
Cannot borrow `x` as mutable more than once at a time
How to bypass “cannot borrow as mutable more than once”?
Cannot borrow as mutable more than once at a time
Passing mutable context into callbacks
Creating a callback system using closures
Execute callbacks like as mutable borrowing from cycle
Callback to mutable self

One way is to use a RefCell, which allows you to mutate things with only &Pen instead of &mut Pen, at the cost of pushing the borrow-checking to runtime. It’s very cheap: there is no allocation, just a single flag test.
The main downside is that violating the rules will result in a panic at runtime. A useful rule of thumb is to never borrow for any longer than necessary (think of them as “single-threaded mutexes”).
use std::cell::RefCell;
fn main() {
println!("Hello, world !");
let p1 = RefCell::new(Pen::new());
{
let mut rp1 = p1.borrow_mut();
rp1.write("Hello");
println!("ink: {}, color: {}", rp1.ink, rp1.color_cmyk);
}
let cb = |text| {
if p1.borrow_mut().write(text) {
println!("{}", text);
}
else {
println!("Out of ink !");
}
};
let cb2 = |text| {
let mut rp1 = p1.borrow_mut();
rp1.write(text);
rp1.ink
};
cb("Hello");
cb("World");
println!("{}", cb2("Hello"));
}
Another way is to set up the callback system to pass in the object that you’re modifying as an argument. The trade-off is then your callback system needs to be aware of this state.
fn main() {
println!("Hello, world !");
let mut p1 = Pen::new();
p1.write("Hello");
println!("ink: {}, color: {}", p1.ink, p1.color_cmyk);
let cb = |p1: &mut Pen, text| if p1.write(text) {
println!("{}", text);
} else {
println!("Out of ink !");
};
let cb2 = |p1: &mut Pen, text| {
p1.write(text);
p1.ink
};
cb(&mut p1, "Hello");
cb(&mut p1, "World");
println!("{}", cb2(&mut p1, "Hello"));
}

Related

How to include a json-rpc in the pow consensus building on Substrate?

In the pow consensus module of substrate, the miner does not dig ore by means of RPC access, how to access?
I don't have idea.
fn mine(
&self,
parent: &BlockId<B>,
pre_hash: &H256,
difficulty: Difficulty,
round: u32,
) -> Result<Option<RawSeal>, String> {
let mut rng = SmallRng::from_rng(&mut thread_rng())
.map_err(|e| format!("Initialize RNG failed for mining: {:?}", e))?;
let key_hash = key_hash(self.client.as_ref(), parent)?;
for _ in 0..round {
let nonce = H256::random_using(&mut rng);
let compute = Compute {
key_hash,
difficulty,
pre_hash: *pre_hash,
nonce,
};
let seal = compute.compute();
if is_valid_hash(&seal.work, difficulty) {
return Ok(Some(seal.encode()))
}
}
Ok(None)
}
I suggest you follow a pattern similar to Kulupu for creating a PoW Substrate blockchain.
In Kulupu, it seems the Substrate service starts mining if it detects your are an "authority":
/// Builds a new service for a full client.
pub fn new_full<C: Send + Default + 'static>(config: Configuration<C, GenesisConfig>, author: Option<&str>, threads: usize, round: u32)
-> Result<impl AbstractService, ServiceError>
{
let is_authority = config.roles.is_authority();
let (builder, inherent_data_providers) = new_full_start!(config, author);
let service = builder
.with_network_protocol(|_| Ok(NodeProtocol::new()))?
.with_finality_proof_provider(|_client, _backend| {
Ok(Arc::new(()) as _)
})?
.build()?;
if is_authority {
for _ in 0..threads {
let proposer = basic_authorship::ProposerFactory {
client: service.client(),
transaction_pool: service.transaction_pool(),
};
consensus_pow::start_mine(
Box::new(service.client().clone()),
service.client(),
kulupu_pow::RandomXAlgorithm::new(service.client()),
proposer,
None,
round,
service.network(),
std::time::Duration::new(2, 0),
service.select_chain().map(|v| v.clone()),
inherent_data_providers.clone(),
);
}
}
Ok(service)
}
In this case, you just need to start your node with the --validator flag and an --author flag with address:
cargo run --release -- --validator --author 0x7e946b7dd192307b4538d664ead95474062ac3738e04b5f3084998b76bc5122d

How to return a result from the Blake2 crate in Rust?

I am struggling getting the hash of the passed file name using the blake2 crate. From the documentation:
extern crate blake2;
use blake2::{Blake2b, Digest};
use std::env;
use std::fs;
use std::io::{self, Read};
const BUFFER_SIZE: usize = 1024;
fn print_result(sum: &[u8]) {
for byte in sum {
print!("{:02x}", byte);
}
}
fn process<D: Digest + Default, R: Read>(reader: &mut R) {
let mut sh = D::default();
let mut buffer = [0u8; BUFFER_SIZE];
loop {
let n = match reader.read(&mut buffer) {
Ok(n) => n,
Err(_) => return,
};
sh.input(&buffer[..n]);
if n == 0 || n < BUFFER_SIZE {
break;
}
}
print_result(&sh.result());
}
fn main() {
let args = env::args();
if args.len() > 1 {
for path in args.skip(1) {
if let Ok(mut file) = fs::File::open(&path) {
process::<Blake2b, _>(&mut file);
}
}
} else {
process::<Blake2b, _>(&mut io::stdin());
}
}
blake-test $ cargo run hoge.txt
Compiling blake-test v0.1.0 (/Users/hoge/blake-test)
Finished dev [unoptimized + debuginfo] target(s) in 0.61s
Running `target/debug/blake-test hoge.txt`
eefea9ae6b7fb678ed54e6d58d46aed9eae6d003f29419948cdb42a44a7016dee3eb566e7e95c68ac7587d5debd516a3b195eed0db84d72819e387d687fd06a6
It can successfully print the the &[u8] slice.
However, I want to receive/return the results instead of printing them.
When you're returning a newly-created object, you have to return it as an owned value.
Borrowed references, such as &[u8] are temporary and can't exist by themselves, they're merely a views of data that has storage in an owned form elsewhere.
You can for example, call .to_vec() on the slice and return Vec<u8>.

How to change the value of a child from a Mirror introspection

I'm doing a bunch of BLE in iOS, which means lots of tight packed C structures being encoded/decoded as byte packets. The following playground snippets illustrate what I'm trying to do generically.
import Foundation
// THE PROBLEM
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
}
sizeof(Thing) // --> 9 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 13> :(
So given a series of fields of varying size, we don't get the "tightest" packing of bytes. Pretty well known and accepted. Given my simple structs, I'd like to be able to arbitrarily encode the fields back to back with no padding or alignment stuff. Relatively easy actually:
// ARBITRARY PACKING
var mirror = Mirror(reflecting: thing)
var output:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as UInt32:
(0...3).forEach { output.append(UInt8((value >> ($0 * 8)) & 0xFF)) }
case let value as UInt8:
output.append(value)
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
output.count // --> 6 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de13> :)
Huzzah! Works as expected. Could probably add a Class around it, or maybe a Protocol extension and have a nice utility. The problem I'm up against is the reverse process:
// ARBITRARY DEPACKING
var input = output.generate()
var thing2 = Thing()
"\(thing2.a), \(thing2.b), \(thing2.c)" // --> "0, 0, 0"
mirror = Mirror(reflecting:thing2)
mirror.children.forEach { (label, child) in
switch child {
case let oldValue as UInt8:
let newValue = input.next()!
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
case let oldValue as UInt32: // do little endian
var newValue:UInt32 = 0
(0...3).forEach {
newValue |= UInt32(input.next()!) << UInt32($0 * 8)
}
print("new value for \(label!) would be \(newValue)")
// *(&child) = newValue // HOW TO DO THIS IN SWIFT??
default:
print("skipping field \(label) of type \(child.dynamicType)")
}
}
Given an unpopulated struct value, I can decode the byte stream appropriately, figure out what the new value would be for each field. What I don't know how to do is to actually update the target struct with the new value. In my example above, I show how I might do it with C, get the pointer to the original child, and then update its value with the new value. I could do it easily in Python/Smalltalk/Ruby. But I don't know how one can do that in Swift.
UPDATE
As suggested in comments, I could do something like the following:
// SPECIFIC DEPACKING
extension GeneratorType where Element == UInt8 {
mutating func _UInt8() -> UInt8 {
return self.next()!
}
mutating func _UInt32() -> UInt32 {
var result:UInt32 = 0
(0...3).forEach {
result |= UInt32(self.next()!) << UInt32($0 * 8)
}
return result
}
}
extension Thing {
init(inout input:IndexingGenerator<[UInt8]>) {
self.init(a: input._UInt8(), b: input._UInt32(), c: input._UInt8())
}
}
input = output.generate()
let thing3 = Thing(input: &input)
"\(thing3.a), \(thing3.b), \(thing3.c)" // --> "66, 3735928495, 19"
Basically, I move the various stream decoding methods to byte stream (i.e. GeneratorType where Element == UInt8), and then I just have to write an initializer that strings those off in the same order and type the struct is defined as. I guess that part, which is essentially "copying" the structure definition itself (and therefore error prone), is what I had hoped to use some sort of introspection to handle. Mirrors are the only real Swift introspection I'm aware of, and it seems pretty limited.
As discussed in the comments, I suspect this is over-clever. Swift includes a lot of types not friendly to this approach. I would focus instead on how to make the boilerplate as easy as possible, without worrying about eliminating it. For example, this is very sloppy, but is in the direction I would probably go:
Start with some helper packer/unpacker functions:
func pack(values: Any...) -> [UInt8]{
var output:[UInt8] = []
for value in values {
switch value {
case let i as UInt32:
(0...3).forEach { output.append(UInt8((i >> ($0 * 8)) & 0xFF)) }
case let i as UInt8:
output.append(i)
default:
assertionFailure("Don't know how to serialize \(value.dynamicType)")
}
}
return output
}
func unpack<T>(bytes: AnyGenerator<UInt8>, inout target: T) throws {
switch target {
case is UInt32:
var newValue: UInt32 = 0
(0...3).forEach {
newValue |= UInt32(bytes.next()!) << UInt32($0 * 8)
}
target = newValue as! T
case is UInt8:
target = bytes.next()! as! T
default:
// Should throw an error here probably
assertionFailure("Don't know how to deserialize \(target.dynamicType)")
}
}
Then just call them:
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var c:UInt8 = 0
func encode() -> [UInt8] {
return pack(a, b, c)
}
static func decode(bytes: [UInt8]) throws -> Thing {
var thing = Thing()
let g = anyGenerator(bytes.generate())
try unpack(g, target: &thing.a)
try unpack(g, target: &thing.b)
try unpack(g, target: &thing.c)
return thing
}
}
A little more thought might be able to make the decode method a little less repetitive, but this is still probably the way I would go, explicitly listing the fields you want to encode rather than trying to introspect them. As you note, Swift introspection is very limited, and it may be that way for a long time. It's mostly used for debugging and logging, not logic.
I have tagged Rob's answer is the official answer. But I'd thought I'd share what I ended up doing as well, inspired by the comments and answers.
First, I fleshed out my "Problem" a little to include a nested structure:
struct Inner {
var ai:UInt16 = 0
var bi:UInt8 = 0
}
struct Thing {
var a:UInt8 = 0
var b:UInt32 = 0
var inner = Inner()
var c:UInt8 = 0
}
sizeof(Thing) // --> 12 :(
var thing = Thing(a: 0x42, b: 0xDEADBEAF, inner: Inner(ai: 0x1122, bi: 0xDD), c: 0x13)
var data = NSData(bytes: &thing, length: sizeof(Thing)) // --> <42000000 afbeadde 2211dd13> :(
For Arbitrary Packing, I stuck with the same generic approach:
protocol Packable {
func packed() -> [UInt8]
}
extension UInt8:Packable {
func packed() -> [UInt8] {
return [self]
}
}
extension UInt16:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF))]
}
}
extension UInt32:Packable {
func packed() -> [UInt8] {
return [(UInt8((self >> 0) & 0xFF)), (UInt8((self >> 8) & 0xFF)), (UInt8((self >> 16) & 0xFF)), (UInt8((self >> 24) & 0xFF))]
}
}
extension Packable {
func packed() -> [UInt8] {
let mirror = Mirror(reflecting:self)
var bytes:[UInt8] = []
mirror.children.forEach { (label, child) in
switch child {
case let value as Packable:
bytes += value.packed()
default:
print("Don't know how to serialize \(child.dynamicType) (field \(label))")
}
}
return bytes
}
}
Being able to "pack" things is as easy adding them to the Packable protocol and telling them to pack themselves. For my cases above, I only need 3 different types of signed integers, but one could add lots more. For example, in my own code, I have some Enums derived from UInt8 which I added the packed method to.
extension Thing:Packable { }
extension Inner:Packable { }
var output = thing.packed()
output.count // --> 9 :)
data = NSData(bytes: &output, length: output.count) // --> <42afbead de2211dd 13> :)
To be able to unpack stuff, I came up with a little bit of support:
protocol UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> Self
}
extension UInt8:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt8 {
return input.next()!
}
}
extension UInt16:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt16 {
return UInt16(input.next()!) | (UInt16(input.next()!) << 8)
}
}
extension UInt32:UnpackablePrimitive {
static func unpack(inout input:IndexingGenerator<[UInt8]>) -> UInt32 {
return UInt32(input.next()!) | (UInt32(input.next()!) << 8) | (UInt32(input.next()!) << 16) | (UInt32(input.next()!) << 24)
}
}
With this, I can then add initializers to my high level structures, e.g.
extension Inner:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(ai: UInt16.unpack(&bytes), bi: UInt8.unpack(&bytes))
}
}
extension Thing:Unpackable {
init(inout packed bytes:IndexingGenerator<[UInt8]>) {
self.init(a: UInt8.unpack(&bytes), b: UInt32.unpack(&bytes), inner: Inner(packed:&bytes), c: UInt8.unpack(&bytes))
}
}
What I liked about this is that these initializers call the default initializer in the same order and types as the structure is defined. So if the structure changes in type or order, I have to revisit the (packed:) initializer. The kids a bit long, but not too.
What I didn't like about this, was having to pass the inout everywhere. I'm honestly not sure what the value is of value based generators, since passing them around you almost always want to share state. Kind of the whole point of reifying an object that captures the position of a stream of data, is to be able to share it. I also don't like having to specify IndexingGenerator directly, but I imagine there's some fu magic that would make that less specific and still work, but I'm not there yet.
I did play with something more pythonic, where I return a tuple of the type and the remainder of a passed array (rather than a stream/generator), but that wasn't nearly as easy to use at the top level init level.
I also tried putting the static methods as extensions on byte based generators, but you have to use a function (would rather have used a computed var with side effects) there whose name doesn't match a type, so you end up with something like
self.init(a: bytes._UInt8(), b: bytes._UInt32(), inner: Inner(packed:&bytes), c: bytes._UInt8())
This is shorter, but doesn't put the type like functions next to the argument names. And would require all kinds of application specific method names to be added as well as one extended the set of UnpackablePrimitives.

Is there a way to count with macros?

I want to create a macro that prints "Hello" a specified number of times. It's used like:
many_greetings!(3); // expands to three `println!("Hello");` statements
The naive way to create that macro is:
macro_rules! many_greetings {
($times:expr) => {{
println!("Hello");
many_greetings!($times - 1);
}};
(0) => ();
}
However, this doesn't work because the compiler does not evaluate expressions; $times - 1 isn't calculated, but fed as a new expression into the macro.
While the ordinary macro system does not enable you to repeat the macro expansion many times, there is no problem with using a for loop in the macro:
macro_rules! many_greetings {
($times:expr) => {{
for _ in 0..$times {
println!("Hello");
}
}};
}
If you really need to repeat the macro, you have to look into procedural macros/compiler plugins (which as of 1.4 are unstable, and a bit harder to write).
Edit: There are probably better ways of implementing this, but I've spent long enough on this for today, so here goes. repeat!, a macro that actually duplicates a block of code a number of times:
main.rs
#![feature(plugin)]
#![plugin(repeat)]
fn main() {
let mut n = 0;
repeat!{ 4 {
println!("hello {}", n);
n += 1;
}};
}
lib.rs
#![feature(plugin_registrar, rustc_private)]
extern crate syntax;
extern crate rustc;
use syntax::codemap::Span;
use syntax::ast::TokenTree;
use syntax::ext::base::{ExtCtxt, MacResult, MacEager, DummyResult};
use rustc::plugin::Registry;
use syntax::util::small_vector::SmallVector;
use syntax::ast::Lit_;
use std::error::Error;
fn expand_repeat(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'static> {
let mut parser = cx.new_parser_from_tts(tts);
let times = match parser.parse_lit() {
Ok(lit) => match lit.node {
Lit_::LitInt(n, _) => n,
_ => {
cx.span_err(lit.span, "Expected literal integer");
return DummyResult::any(sp);
}
},
Err(e) => {
cx.span_err(sp, e.description());
return DummyResult::any(sp);
}
};
let res = parser.parse_block();
match res {
Ok(block) => {
let mut stmts = SmallVector::many(block.stmts.clone());
for _ in 1..times {
let rep_stmts = SmallVector::many(block.stmts.clone());
stmts.push_all(rep_stmts);
}
MacEager::stmts(stmts)
}
Err(e) => {
cx.span_err(sp, e.description());
DummyResult::any(sp)
}
}
}
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_macro("repeat", expand_repeat);
}
added to Cargo.toml
[lib]
name = "repeat"
plugin = true
Note that if we really don't want to do looping, but expanding at compile-time, we have to do things like requiring literal numbers. After all, we are not able to evaluate variables and function calls that reference other parts of the program at compile time.
As the other answers already said: no, you can't count like this with declarative macros (macro_rules!).
But you can implement the many_greetings! example as a procedural macro. procedural macros were stabilized a while ago, so the definition works on stable. However, we can't yet expand macros into statements on stable -- that's what the #![feature(proc_macro_hygiene)] is for.
This looks like a lot of code, but most code is just error handling, so it's not that complicated!
examples/main.rs
#![feature(proc_macro_hygiene)]
use count_proc_macro::many_greetings;
fn main() {
many_greetings!(3);
}
Cargo.toml
[package]
name = "count-proc-macro"
version = "0.1.0"
authors = ["me"]
edition = "2018"
[lib]
proc-macro = true
[dependencies]
quote = "0.6"
src/lib.rs
extern crate proc_macro;
use std::iter;
use proc_macro::{Span, TokenStream, TokenTree};
use quote::{quote, quote_spanned};
/// Expands into multiple `println!("Hello");` statements. E.g.
/// `many_greetings!(3);` will expand into three `println`s.
#[proc_macro]
pub fn many_greetings(input: TokenStream) -> TokenStream {
let tokens = input.into_iter().collect::<Vec<_>>();
// Make sure at least one token is provided.
if tokens.is_empty() {
return err(Span::call_site(), "expected integer, found no input");
}
// Make sure we don't have too many tokens.
if tokens.len() > 1 {
return err(tokens[1].span(), "unexpected second token");
}
// Get the number from our token.
let count = match &tokens[0] {
TokenTree::Literal(lit) => {
// Unfortunately, `Literal` doesn't have nice methods right now, so
// the easiest way for us to get an integer out of it is to convert
// it into string and parse it again.
if let Ok(count) = lit.to_string().parse::<usize>() {
count
} else {
let msg = format!("expected unsigned integer, found `{}`", lit);
return err(lit.span(), msg);
}
}
other => {
let msg = format!("expected integer literal, found `{}`", other);
return err(other.span(), msg);
}
};
// Return multiple `println` statements.
iter::repeat(quote! { println!("Hello"); })
.map(TokenStream::from)
.take(count)
.collect()
}
/// Report an error with the given `span` and message.
fn err(span: Span, msg: impl Into<String>) -> TokenStream {
let msg = msg.into();
quote_spanned!(span.into()=> {
compile_error!(#msg);
}).into()
}
Running cargo run --example main prints three "Hello"s.
For those looking for a way to do this, there is also the seq_macro crate.
It is fairly easy to use and works out of the box with stable Rust.
use seq_macro::seq;
macro_rules! many_greetings {
($times:literal) => {
seq!{ N in 0..$times {
println!("Hello");
}}
};
}
fn main() {
many_greetings!(3);
many_greetings!(12);
}
As far as I know, no. The macro language is based on pattern matching and variable substitution, and only evaluates macros.
Now, you can implement counting with evaluation: it just is boring... see the playpen
macro_rules! many_greetings {
(3) => {{
println!("Hello");
many_greetings!(2);
}};
(2) => {{
println!("Hello");
many_greetings!(1);
}};
(1) => {{
println!("Hello");
many_greetings!(0);
}};
(0) => ();
}
Based on this, I am pretty sure one could invent a set of macro to "count" and invoke various operations at each step (with the count).

HOWTO: Idiomatic Rust for callbacks with gtk (rust-gnome)

I am currently learning Rust and looking to use it for developing a GUI
based application with GTK+. My problem relates to registering callbacks to
respond to GTK events/signals and mutating state within those callbacks.
I have a working but inelegant solution, so I would like to ask if there
is a cleaner, more idiomatic solution.
I have implemented my code as a struct with method implementations, where
the struct maintains references to the GTK widgets along with other state
that it needs. It constructs a closure that is passed to the
GtkWidget::connect* functions in order to receive events, draw to a
canvas, etc. This can cause problems with the borrow checker, as I will now
explain. I have some working but (IMHO) non-ideal code that I will
show.
Initial, non-working solution:
#![cfg_attr(not(feature = "gtk_3_10"), allow(unused_variables, unused_mut))]
extern crate gtk;
extern crate cairo;
use gtk::traits::*;
use gtk::signal::Inhibit;
use cairo::{Context, RectangleInt};
struct RenderingAPITestWindow {
window: gtk::Window,
drawing_area: gtk::DrawingArea,
width: i32,
height: i32
}
impl RenderingAPITestWindow {
fn new(width: i32, height: i32) -> RenderingAPITestWindow {
let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
let drawing_area = gtk::DrawingArea::new().unwrap();
drawing_area.set_size_request(width, height);
window.set_title("Cairo API test");
window.add(&drawing_area);
let instance = RenderingAPITestWindow{window: window,
drawing_area: drawing_area,
width: width,
height: height,
};
instance.drawing_area.connect_draw(|widget, cairo_context| {
instance.on_draw(cairo_context);
instance.drawing_area.queue_draw();
Inhibit(true)
});
instance.drawing_area.connect_size_allocate(|widget, rect| {
instance.on_size_allocate(rect);
});
instance.window.show_all();
return instance;
}
fn exit_on_close(&self) {
self.window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(true)
});
}
fn on_draw(&mut self, cairo_ctx: Context) {
cairo_ctx.save();
cairo_ctx.move_to(50.0, (self.height as f64) * 0.5);
cairo_ctx.set_font_size(18.0);
cairo_ctx.show_text("The only curse they could afford to put on a tomb these days was 'Bugger Off'. --PTerry");
cairo_ctx.restore();
}
fn on_size_allocate(&mut self, rect: &RectangleInt) {
self.width = rect.width as i32;
self.height = rect.height as i32;
}
}
fn main() {
gtk::init().unwrap_or_else(|_| panic!("Failed to initialize GTK."));
println!("Major: {}, Minor: {}", gtk::get_major_version(), gtk::get_minor_version());
let window = RenderingAPITestWindow::new(800, 500);
window.exit_on_close();
gtk::main();
}
The above fails to compile as the closures with
RenderingAPITestWindow::new that are created and passed to calls to
GtkWidget::connect* methods attempt to borrow instance. The
compiler states that the closures may outlive the function in which
they are declared and that instance is owned by the outer function,
hence the problem. Given that GTK may keep a reference to these closures
around for an unspecified amount of time, we need an approach in which the
lifetime can be determined at runtime, hence my next stab at the problem
in which the RenderingAPITestWindow instance is wrapped in
Rc<RefCell<...>>.
Wrapping the RenderingAPITestWindow instance compiles but dies at runtime:
#![cfg_attr(not(feature = "gtk_3_10"), allow(unused_variables, unused_mut))]
extern crate gtk;
extern crate cairo;
use std::rc::Rc;
use std::cell::RefCell;
use gtk::traits::*;
use gtk::signal::Inhibit;
use cairo::{Context, RectangleInt};
struct RenderingAPITestWindow {
window: gtk::Window,
drawing_area: gtk::DrawingArea,
width: i32,
height: i32
}
impl RenderingAPITestWindow {
fn new(width: i32, height: i32) -> Rc<RefCell<RenderingAPITestWindow>> {
let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
let drawing_area = gtk::DrawingArea::new().unwrap();
drawing_area.set_size_request(width, height);
window.set_title("Cairo API test");
window.add(&drawing_area);
let instance = RenderingAPITestWindow{window: window,
drawing_area: drawing_area,
width: width,
height: height,
};
let wrapped_instance = Rc::new(RefCell::new(instance));
let wrapped_instance_for_draw = wrapped_instance.clone();
wrapped_instance.borrow().drawing_area.connect_draw(move |widget, cairo_context| {
wrapped_instance_for_draw.borrow_mut().on_draw(cairo_context);
wrapped_instance_for_draw.borrow().drawing_area.queue_draw();
Inhibit(true)
});
let wrapped_instance_for_sizealloc = wrapped_instance.clone();
wrapped_instance.borrow().drawing_area.connect_size_allocate(move |widget, rect| {
wrapped_instance_for_sizealloc.borrow_mut().on_size_allocate(rect);
});
wrapped_instance.borrow().window.show_all();
return wrapped_instance;
}
fn exit_on_close(&self) {
self.window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(true)
});
}
fn on_draw(&mut self, cairo_ctx: Context) {
cairo_ctx.save();
cairo_ctx.move_to(50.0, (self.height as f64) * 0.5);
cairo_ctx.set_font_size(18.0);
cairo_ctx.show_text("The only curse they could afford to put on a tomb these days was 'Bugger Off'. --PTerry");
cairo_ctx.restore();
}
fn on_size_allocate(&mut self, rect: &RectangleInt) {
self.width = rect.width as i32;
self.height = rect.height as i32;
}
}
fn main() {
gtk::init().unwrap_or_else(|_| panic!("Failed to initialize GTK."));
println!("Major: {}, Minor: {}", gtk::get_major_version(), gtk::get_minor_version());
let wrapped_window = RenderingAPITestWindow::new(800, 500);
wrapped_window.borrow().exit_on_close();
gtk::main();
}
The above solution compiles but its not particularly pretty:
RenderingAPITestWindow::new returns an
Rc<RefCell<RenderingAPITestWindow>> rather than a
RenderingAPITestWindow
Accessing fields and methods of RenderingAPITestWindow is complicated
by the fact that the Rc<RefCell<...>> must be opened up; it now requires
wrapped_instance.borrow().some_method(...) rather than just
instance.some_method(...)
Each closure requires it's own clone of wrapped_instance; attempting
to use wrapped_instance would attempt to borrow an object -- the
wrapper rather than the RenderingAPITestWindow this time -- that is
owned by RenderingAPITestWindow::new as before
While the above compiles, it dies at runtime with:
thread '<main>' panicked at 'RefCell<T> already borrowed', ../src/libcore/cell.rs:442
An unknown error occurred
This is due to the fact that the call to window.show_all() causes GTK to
initialise the widget hierarchy, resulting in the drawing area widget
receiving a size-allocate event. Accessing the window to call
show_all() required that the Rc<RefCell<...>> is opened (hence
wrapped_instance.borrow().window.show_all();) and the instance
borrowed. Before the borrow ends when show_all() returns, GTK invokes the
drawing area's size-allocate event handler, which causes the closure
connected to it (4 lines above) to be invoked. The closure attempts to
borrow a mutable reference to the RenderingAPITestWindow instance
(wrapped_instance_for_sizealloc.borrow_mut().on_size_allocate(rect);)
in order to invoke the on_size_allocate method. This attempts to borrow a
mutable reference, while the first immutable reference is still in scope.
This second borrow causes the run-time panic.
The working but - IMHO - inelegant solution that I have managed to get
working so far is to split RenderingAPITestWindow into two structs, with
the mutable state that is to modified by the callbacks moved into a
separate struct.
Working but inelegant solution that splits the RenderingAPITestWindow struct:
#![cfg_attr(not(feature = "gtk_3_10"), allow(unused_variables, unused_mut))]
extern crate gtk;
extern crate cairo;
use std::rc::Rc;
use std::cell::RefCell;
use gtk::traits::*;
use gtk::signal::Inhibit;
use cairo::{Context, RectangleInt};
struct RenderingAPITestWindowState {
width: i32,
height: i32
}
impl RenderingAPITestWindowState {
fn new(width: i32, height: i32) -> RenderingAPITestWindowState {
return RenderingAPITestWindowState{width: width, height: height};
}
fn on_draw(&mut self, cairo_ctx: Context) {
cairo_ctx.save();
cairo_ctx.move_to(50.0, (self.height as f64) * 0.5);
cairo_ctx.set_font_size(18.0);
cairo_ctx.show_text("The only curse they could afford to put on a tomb these days was 'Bugger Off'. --PTerry");
cairo_ctx.restore();
}
fn on_size_allocate(&mut self, rect: &RectangleInt) {
self.width = rect.width as i32;
self.height = rect.height as i32;
}
}
struct RenderingAPITestWindow {
window: gtk::Window,
drawing_area: gtk::DrawingArea,
state: Rc<RefCell<RenderingAPITestWindowState>>
}
impl RenderingAPITestWindow {
fn new(width: i32, height: i32) -> Rc<RefCell<RenderingAPITestWindow>> {
let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
let drawing_area = gtk::DrawingArea::new().unwrap();
drawing_area.set_size_request(width, height);
window.set_title("Cairo API test");
window.add(&drawing_area);
let wrapped_state = Rc::new(RefCell::new(RenderingAPITestWindowState::new(width, height)))
;
let instance = RenderingAPITestWindow{window: window,
drawing_area: drawing_area,
state: wrapped_state.clone()
};
let wrapped_instance = Rc::new(RefCell::new(instance));
let wrapped_state_for_draw = wrapped_state.clone();
let wrapped_instance_for_draw = wrapped_instance.clone();
wrapped_instance.borrow().drawing_area.connect_draw(move |widget, cairo_context| {
wrapped_state_for_draw.borrow_mut().on_draw(cairo_context);
wrapped_instance_for_draw.borrow().drawing_area.queue_draw();
Inhibit(true)
});
let wrapped_state_for_sizealloc = wrapped_state.clone();
wrapped_instance.borrow().drawing_area.connect_size_allocate(move |widget, rect| {
wrapped_state_for_sizealloc.borrow_mut().on_size_allocate(rect);
});
wrapped_instance.borrow().window.show_all();
return wrapped_instance;
}
fn exit_on_close(&self) {
self.window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(true)
});
}
}
fn main() {
gtk::init().unwrap_or_else(|_| panic!("Failed to initialize GTK."));
println!("Major: {}, Minor: {}", gtk::get_major_version(), gtk::get_minor_version());
let wrapped_window = RenderingAPITestWindow::new(800, 500);
wrapped_window.borrow().exit_on_close();
gtk::main();
}
While the above code works as required, I would like to find a better way
for going forward; I would like to ask if anyone knows a better approach as
the above complicates the programming process a fair bit, with the need to
use Rc<RefCell<...>> and split structs to satisfy Rust's borrowing rules.
Here's a working version that I came up with:
#![cfg_attr(not(feature = "gtk_3_10"), allow(unused_variables, unused_mut))]
extern crate gtk;
extern crate cairo;
use std::rc::Rc;
use std::cell::RefCell;
use gtk::traits::*;
use gtk::signal::Inhibit;
use cairo::{Context, RectangleInt};
struct RenderingAPITestWindow {
window: gtk::Window,
drawing_area: gtk::DrawingArea,
state: RefCell<RenderingState>,
}
struct RenderingState {
width: i32,
height: i32,
}
impl RenderingAPITestWindow {
fn new(width: i32, height: i32) -> Rc<RenderingAPITestWindow> {
let window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
let drawing_area = gtk::DrawingArea::new().unwrap();
drawing_area.set_size_request(width, height);
window.set_title("Cairo API test");
window.add(&drawing_area);
let instance = Rc::new(RenderingAPITestWindow {
window: window,
drawing_area: drawing_area,
state: RefCell::new(RenderingState {
width: width,
height: height,
}),
});
{
let instance2 = instance.clone();
instance.drawing_area.connect_draw(move |widget, cairo_context| {
instance2.state.borrow().on_draw(cairo_context);
instance2.drawing_area.queue_draw();
Inhibit(true)
});
}
{
let instance2 = instance.clone();
instance.drawing_area.connect_size_allocate(move |widget, rect| {
instance2.state.borrow_mut().on_size_allocate(rect);
});
}
instance.window.show_all();
instance
}
fn exit_on_close(&self) {
self.window.connect_delete_event(|_, _| {
gtk::main_quit();
Inhibit(true)
});
}
}
impl RenderingState {
fn on_draw(&self, cairo_ctx: Context) {
cairo_ctx.save();
cairo_ctx.move_to(50.0, (self.height as f64) * 0.5);
cairo_ctx.set_font_size(18.0);
cairo_ctx.show_text("The only curse they could afford to put on a tomb these days was 'Bugger Off'. --PTerry");
cairo_ctx.restore();
}
fn on_size_allocate(&mut self, rect: &RectangleInt) {
self.width = rect.width as i32;
self.height = rect.height as i32;
}
}
fn main() {
gtk::init().unwrap_or_else(|_| panic!("Failed to initialize GTK."));
println!("Major: {}, Minor: {}", gtk::get_major_version(), gtk::get_minor_version());
let window = RenderingAPITestWindow::new(800, 500);
window.exit_on_close();
gtk::main();
}
I arrived at this through a few observations:
The instance is being shared across multiple closures for an undetermined amount of time. Rc is the right answer to that scenario because it provides shared ownership. Rc is very ergonomic to use; it works like any other pointer type.
The only part of instance that is actually mutated is your state. Since your instance is being shared, it cannot be borrowed mutably using the standard &mut pointer. Therefore, you must use interior mutability. This is what RefCell provides. Note though, that you only need to use RefCell on the state you're mutating. So this still separates out the state into a separate struct, but it works nicely IMO.
A possible modification to this code is to add #[derive(Clone, Copy)] to the definition of the RenderingState struct. Since it can be Copy (because all of its component types are Copy), you can use Cell instead of RefCell.