How can I use a class (struct + impl) that's found in another class - class

I want to use a class that's created inside another class in my main method. The Packagemnm class has a vector of Mnm classes and you can add to them using the add() method which takes in a Mnm object and adds it to the vector. When I try to compile this cargo gives the following error:
Compiling candy v0.1.0 (/mnt/c/Users/mateo/Coding/candy)
error[E0433]: failed to resolve: use of undeclared type `Mnm`
--> src/main.rs:8:14
|
8 | pack.add(Mnm::new());
| ^^^ not found in this scope
|
note: struct `crate::packagemnm::mnm::Mnm` exists but is inaccessible
--> src/packagemnm/mnm.rs:12:1
|
12 | pub struct Mnm {
| ^^^^^^^^^^^^^^ not accessible
For more information about this error, try `rustc --explain E0433`.
error: could not compile `candy` due to previous error
I don't really understand it because the Mnm struct is already included inside the mod.rs file. I tried including it but the compiler told me that it was inaccessible.
My src directory looks like this where the mod.rs file contains the Packagemnm class
src/
├── main.rs
└── packagemnm
├── mnm.rs
└── mod.rs
main.rs:
mod packagemnm;
fn main() {
// let red = mnm::Mnm::new(mnm::Colors::RED);
let pack = packagemnm::Packagemnm::new();
pack.add(Mnm::new());
}
packagemnm/mod.rs
mod mnm;
use mnm::{Colors, Mnm};
pub struct Packagemnm {
m_list: Vec<Mnm>,
red: i32,
blue: i32,
green: i32,
brown: i32,
yellow: i32,
orange: i32
}
impl Packagemnm {
pub fn new() -> Packagemnm {
Packagemnm {
m_list: Vec::new(),
red: 0,
blue: 0,
green: 0,
brown: 0,
yellow: 0,
orange: 0
}
}
pub fn add(&mut self, obj: Mnm) {
self.m_list.push(obj);
match obj.get_color() {
Colors::RED => self.red+=1,
Colors::BLUE => self.blue+=1,
Colors::GREEN => self.green+=1,
Colors::YELLOW => self.yellow+=1,
Colors::ORANGE => self.orange+=1,
Colors::BROWN => self.brown+=1,
_ => ()
}
}
pub fn to_string(&mut self) -> String {
let s: String = format!("Red: {}\nBlue: {}\nGreen: {}\nBrown: {}\nYellow: {}\nOrange: {}",
self.red, self.blue, self.green, self.brown, self.yellow, self.orange);
s
}
}
packagemnm/mnm.rs
#[derive(Copy, Clone)]
pub enum Colors {
RED = 0,
BLUE,
GREEN,
BROWN,
YELLOW,
ORANGE
}
#[derive(Copy, Clone)]
pub struct Mnm {
m_color: Colors
}
impl Mnm {
pub fn new(c: Colors) -> Mnm {
Mnm {
m_color: c
}
}
pub fn get_color(&self) -> Colors {
self.m_color
}
}

I believe the the mod keyword in rust only declares the additional file; you still need to actually import the functions from that file! Try something like:
mod packagemnm;
use packagemnm::mnm::*;
fn main() {
// let red = mnm::Mnm::new(mnm::Colors::RED);
let pack = packagemnm::Packagemnm::new();
pack.add(Mnm::new());
}
and then change the mod mnm declaration in mod.rs to
pub mod mnm;

Related

the trait `LoadConnection` is not implemented for `&diesel::PgConnection`

I want to create a rest api with rust and can't make it work.
My relevant code so far:
In the main.rs:
#[actix_web::main]
async fn main() -> std::io::Result<()> {
// Loading .env into environment variable.
dotenv::dotenv().ok();
env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
// set up database connection pool
let database_url = std::env::var("DATABASE_URL").expect("DATABASE_URL");
let manager = ConnectionManager::<PgConnection>::new(database_url);
let pool: DbPool = r2d2::Pool::builder()
.test_on_check_out(true)
.build(manager)
.expect("Could not build connection pool");
let port = std::env::var("PORT").expect("$PORT is not set.");
HttpServer::new(move || {
App::new()
.app_data(web::Data::new(pool.clone()))
.wrap(middleware::Logger::default())
.route("/", web::get().to(|| async { "Actix REST API" }))
.service(handlers::common::houses::index)
})
.bind(("0.0.0.0", port.parse().unwrap()))?
.run()
.await
}
The schema:
diesel::table! {
houses (id) {
id -> Int4,
user_id -> Varchar,
street -> Varchar,
figure -> Varchar,
floor -> Varchar,
create_date -> Timestamp,
update_date -> Timestamp,
is_deleted -> Bool,
}
}
The model:
#[derive(Debug, Serialize, Deserialize, Queryable)]
pub struct House {
pub id: i32,
pub user_id: String,
pub street: String,
pub figure: String,
pub floor: String,
pub create_date: chrono::NaiveDateTime,
pub update_date: chrono::NaiveDateTime,
pub is_deleted: bool,
}
The handler:
#[get("/houses")]
async fn index(pool: web::Data<DbPool>) -> Result<HttpResponse, Error> {
let houses = web::block(move || {
let conn = &pool.get()?;
find_all(&conn)
})
.await?
.map_err(actix_web::error::ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json(houses))
}
fn find_all(conn: &PgConnection) -> Result<Vec<House>, DbError> {
use crate::schemas::common::houses::houses::dsl::*;
let items =houses.load::<House>(&mut conn)?;
Ok(items)
}
The dependencies are:
[dependencies]
actix-web = "4"
chrono = { version = "0.4.19", features = ["serde"] }
diesel = { version = "2.0.3", features = ["postgres", "r2d2", "chrono"] }
dotenv = "0.15.0"
env_logger = "0.10.0"
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0"`
It keeps giving an error, and I don't understand why.
The error is:
`error[E0277]: the trait bound `&diesel::PgConnection: LoadConnection` is not satisfied src\handlers\common\houses.rs:25:37
| 25 | let items =houses.load::<House>(&mut conn)?;
| ---- -^^^^^^^^
| | | the trait `LoadConnection` is not implemented for `&diesel::PgConnection` help: consider removing the leading `&`-reference required by a bound introduced by this call
| note: required for `table` to implement `LoadQuery<'_, &diesel::PgConnection, House>` note: required by a bound in `diesel::RunQueryDsl::load`
I've seen a similar error with the diesel version 1.4, but I think that this version is different.
Plus I'm starting with rust and I'm a little lost in general at the moment.
I was hopping someone knows what the problem is and how to fix it.
PgConnection implements LoadConnection but &PgConnection does not (note the extra &).
Make conn mutable and pass as a mutable reference:
#[get("/houses")]
async fn index(pool: web::Data<DbPool>) -> Result<HttpResponse, Error> {
let houses = web::block(move || {
let mut conn = pool.get()?; // <------------
find_all(&mut conn) // <------------
})
.await?
.map_err(actix_web::error::ErrorInternalServerError)?;
Ok(HttpResponse::Ok().json(houses))
}
fn find_all(conn: &mut PgConnection) -> Result<Vec<House>, DbError> {
// ^^^ <------------
use crate::schemas::common::houses::houses::dsl::*;
let items = houses.load::<House>(conn)?; // <------------
Ok(items)
}

DateTime<Utc> compiles but not DateTime<Local> querying a table with a column defined as timestamp with time zone

I have a postgresql-table with a column defined as timestamp with time zone. The table is mapped to this struct:
#[derive(Serialize, Queryable)]
pub struct Location {
pub publication_time: DateTime<Utc>,
pub id: i32,
pub name: String,
pub latitude: BigDecimal,
pub longitude: BigDecimal,
}
The schema have this definition:
table! {
locations {
publication_time -> Timestamptz,
id -> Integer,
name -> Text,
latitude -> Numeric,
longitude -> Numeric,
}
}
(partial) Cargo.toml:
serde = "1.0.125"
serde_json = "1.0.64"
serde_derive = "1.0.125"
diesel = { version = "1.4.6", features = ["postgres", "r2d2", "chrono", "numeric"] }
bigdecimal = { version = "0.1.0", features = ["serde"] }
chrono = { version = "0.4.19", features = ["serde"] }
The function that queries the database:
fn get_all_locations(pool: web::Data<Pool>) -> Result<Vec<Location>, diesel::result::Error> {
let conn = pool.get().unwrap();
let items = locations.load::<Location>(&conn)?;
Ok(items)
}
This is then serialized to a JSON-array using serde_json. The DateTime in the database is 2021-04-08 15:02:02.514+02. When DateTime is Utc the program compiles fine, but the DateTime shown in UTC like 2021-04-08T13:02:02.514Z. I changed publication_time to DateTime<Local> to retain the time zone information but then cargo build fails with:
error[E0277]: the trait bound `DateTime<Local>: FromSql<diesel::sql_types::Timestamptz, Pg>` is not satisfied
--> src/controller.rs:21:27
|
21 | let items = locations.load::<Location>(&conn)?;
| ^^^^ the trait `FromSql<diesel::sql_types::Timestamptz, Pg>` is not implemented for `DateTime<Local>`
|
= help: the following implementations were found:
<DateTime<Utc> as FromSql<diesel::sql_types::Timestamptz, Pg>>
= note: required because of the requirements on the impl of `diesel::Queryable<diesel::sql_types::Timestamptz, Pg>` for `DateTime<Local>`
= note: 2 redundant requirements hidden
= note: required because of the requirements on the impl of `diesel::Queryable<(diesel::sql_types::Timestamptz, diesel::sql_types::Integer, diesel::sql_types::Text, diesel::sql_types::Numeric, diesel::sql_types::Numeric), Pg>` for `models::Location`
= note: required because of the requirements on the impl of `LoadQuery<_, models::Location>` for `locations::table`
I have another program that insert to this table and this works and the only difference is derive(Deserialize, Insertable).
#[derive(Deserialize, Insertable)]
pub struct Location {
pub publication_time: DateTime<Local>,
pub id: i32,
pub name: String,
pub latitude: BigDecimal,
pub longitude: BigDecimal,
}
Mapping a Timestamptz field to a DateTime<Local> is not supported by diesel itself, as it only provides the corresponding impl for DateTime<Utc>.
You can work this around by using the #[diesel(deserialize_as = "…")] attribute on the corresponding field and providing your own deserialization wrapper:
#[derive(Serialize, Queryable)]
pub struct Location {
#[diesel(deserialize_as = "MyDateTimeWrapper")]
pub publication_time: DateTime<Local>,
pub id: i32,
pub name: String,
pub latitude: BigDecimal,
pub longitude: BigDecimal,
}
pub struct MyDatetimeWrapper(DateTime<Local>);
impl Into<DateTime<Local>> for MyDatetimeWrapper {
fn into(self) -> DateTime<Local> {
self.0
}
}
impl<DB, ST> Queryable<ST, DB> for MyDateTimeWrapper
where
DB: Backend,
DateTime<Utc>: Queryable<ST, DB>,
{
type Row = <DateTime<Utc> as Queryable<ST, DB>>::Row;
fn build(row: Self::Row) -> Self {
Self(<DateTime<Utc> as Queryable<ST, DB>>::build(row).with_timezone(&Local))
}
}

Can I restrict an enum to certain cases of another enum?

Say I have a bakery and an inventory of ingredients:
enum Ingredient {
case flower = 1
case sugar = 2
case yeast = 3
case eggs = 4
case milk = 5
case almonds = 6
case chocolate = 7
case salt = 8
}
A case's rawValue represents the inventory number.
Then I have two recipes:
Chocolate Cake:
500g flower
300g sugar
3 eggs
200ml milk
200g chocolate
Almond Cake:
300g flower
200g sugar
20g yeast
200g almonds
5 eggs
2g salt
Now I define a function
func bake(with ingredients: [Ingredient]) -> Cake
Of course I trust my employees, but I still want to make sure they only use the right ingredients to bake a cake. 😉
I could do this by defining two separate enums like this:
enum ChocolateCakeIngredient {
case flower
case sugar
case eggs
case milk
case chocolate
}
enum AlmondCakeIngredient {
case flower
case sugar
case yeast
case eggs
case almonds
case salt
}
and bake a cake like this:
// in chocolate cake class / struct:
func bake(with ingredients: [ChocolateCakeIngredient]) -> ChocolateCake
// in almond cake class / struct:
func bake(with ingredients: [AlmondCakeIngredient]) -> AlmondCake
But then I would have to redefine the same ingredients over and over again as many ingredients are used for both cakes. I really don't want to do that - especially as there are inventory numbers attached to the enum cases as rawValues.
That leads me to the question if there is a way in Swift to restrict an enum to certain cases of another enum? Something like (pseudo code):
enum ChocolateCakeIngredient: Ingredient {
allowedCases:
case flower
case sugar
case eggs
case milk
case chocolate
}
enum AlmondCakeIngredient: Ingredient {
allowedCases:
case flower
case sugar
case yeast
case eggs
case almonds
case salt
}
Is a composition like this possible? How can I do it?
Or maybe there is another pattern I can use for this scenario?
Update
From all the comments and answers to this question I figured that the example I picked for this question was a little inappropriate as it didn't boil down the essence of the problem and left a loophole regarding type safety.
As all posts on this page relate to this particular example, I created a new question on Stackoverflow with an example that's easier to understand and hits the nail on its head:
➡️ Same question with a more specific example
I think you should list ingredients for specific recipes as:
let chocolateCakeIngredients: [Ingredient] = [.flower, ...]
and then just check if that list contains the required ingredient.
You could do something like this in Swift:
enum Ingredients {
struct Flower { }
struct Sugar { }
struct Yeast { }
struct Eggs { }
struct Milc { }
}
protocol ChocolateCakeIngredient { }
extension Sugar: ChocolateCakeIngredient { }
extension Eggs: ChocolateCakeIngredient { }
...
func bake(ingredients: [ChocolateCakeIngredient]) { }
In this example i am using the enum Ingredients as a namespace for all my ingedients. This also helps with code completion.
Then, create a protocol for each Recipe and conform the ingredients that go in that recipe to that protocol.
While this should solve your question, I am not sure that you should do this. This (and also your pseudo-code) will enforce that no one can pass a ingredient that does not belong into a chocolate cake when baking one. It will, however, not prohibit anyone to try and call bake(with ingredients:) with an empty array or something similar. Because of that, you will not actually gain any safety by your design.
An Alternative Approach: Using an Option Set Type
Or maybe there is another pattern I can use for this scenario?
Another approach is letting your Ingredient be an OptionSet type (a type conforming to the protocol OptionsSet):
https://developer.apple.com/reference/swift/optionset
E.g.
struct Ingredients: OptionSet {
let rawValue: UInt8
static let flower = Ingredients(rawValue: 1 << 0) //0b00000001
static let sugar = Ingredients(rawValue: 1 << 1) //0b00000010
static let yeast = Ingredients(rawValue: 1 << 2) //0b00000100
static let eggs = Ingredients(rawValue: 1 << 3) //0b00001000
static let milk = Ingredients(rawValue: 1 << 4) //0b00010000
static let almonds = Ingredients(rawValue: 1 << 5) //0b00100000
static let chocolate = Ingredients(rawValue: 1 << 6) //0b01000000
static let salt = Ingredients(rawValue: 1 << 7) //0b10000000
// some given ingredient sets
static let chocolateCakeIngredients: Ingredients =
[.flower, .sugar, .eggs, .milk, .chocolate]
static let almondCakeIngredients: Ingredients =
[.flower, .sugar, .yeast, .eggs, .almonds, .salt]
}
Applied to your bake(with:) example, where the employee/dev attempts to implement the baking of a chocolate cake in the body of bake(with:):
/* dummy cake */
struct Cake {
var ingredients: Ingredients
init(_ ingredients: Ingredients) { self.ingredients = ingredients }
}
func bake(with ingredients: Ingredients) -> Cake? {
// lets (attempt to) bake a chokolate cake
let chocolateCakeWithIngredients: Ingredients =
[.flower, .sugar, .yeast, .milk, .chocolate]
// ^^^^^ ups, employee misplaced .eggs for .yeast!
/* alternatively, add ingredients one at a time / subset at a time
var chocolateCakeWithIngredients: Ingredients = []
chocolateCakeWithIngredients.formUnion(.yeast) // ups, employee misplaced .eggs for .yeast!
chocolateCakeWithIngredients.formUnion([.flower, .sugar, .milk, .chocolate]) */
/* runtime check that ingredients are valid */
/* ---------------------------------------- */
// one alternative, invalidate the cake baking by nil return if
// invalid ingredients are used
guard ingredients.contains(chocolateCakeWithIngredients) else { return nil }
return Cake(chocolateCakeWithIngredients)
/* ... or remove invalid ingredients prior to baking the cake
return Cake(chocolateCakeWithIngredients.intersection(ingredients)) */
/* ... or, make bake(with:) a throwing function, which throws and error
case containing the set of invalid ingredients for some given attempted baking */
}
Along with a call to bake(with:) using the given available chocolate cake ingredients:
if let cake = bake(with: Ingredients.chocolateCakeIngredients) {
print("We baked a chocolate cake!")
}
else {
print("Invalid ingredients used for the chocolate cake ...")
} // Invalid ingredients used for the chocolate cake ...
I don't believe it is possible to perform a check like this at compile time. Here is one way to structure your code to do this at runtime:
enum Ingredient: Int {
case flour = 1
case sugar = 2
case yeast = 3
case eggs = 4
case milk = 5
case almonds = 6
case chocolate = 7
case salt = 8
}
protocol Cake {
init()
static var validIngredients: [Ingredient] { get }
}
extension Cake {
static func areIngredientsAllowed(_ ingredients: [Ingredient]) -> Bool {
for ingredient in ingredients {
if !validIngredients.contains(ingredient) {
return false
}
}
return true
}
}
class ChocolateCake: Cake {
required init() {}
static var validIngredients: [Ingredient] = [.flour, .sugar, .eggs, .milk, .chocolate]
}
class AlmondCake: Cake {
required init() {}
static var validIngredients: [Ingredient] = [.flour, .sugar, .yeast, .eggs, .almonds, .salt]
}
The bake method looks like this:
func bake<C: Cake>(ingredients: [Ingredient]) -> C {
guard C.areIngredientsAllowed(ingredients) else {
fatalError()
}
let cake = C()
// TODO: Let's bake!
return cake
}
Now I can say:
let almondCake: AlmondCake = bake(ingredients: ingredients)
... and be sure that only valid ingredients were used.
Static Solution:
If the recipe quantities are always the same, you can use a function in the enum:
enum Ingredient {
case chocolate
case almond
func bake() -> Cake {
switch self {
case chocolate:
print("chocolate")
/*
return a Chocolate Cake based on:
500g flower
300g sugar
3 eggs
200ml milk
200g chocolate
*/
case almond:
print("almond")
/*
return an Almond Cake based on:
300g flower
200g sugar
20g yeast
200g almonds
5 eggs
2g salt
*/
}
}
}
Usage:
// bake chocolate cake
let bakedChocolateCake = Ingredient.chocolate.bake()
// bake a almond cake
let bakedAlmondCake = Ingredient.almond.bake()
Dynamic Solution:
If the recipe quantities are changeable -and that's what I assume-, I cheated a little bit by using a separated model class :)
It will be as the following:
class Recipe {
private var flower = 0
private var sugar = 0
private var yeast = 0
private var eggs = 0
private var milk = 0
private var almonds = 0
private var chocolate = 0
private var salt = 0
// init for creating a chocolate cake:
init(flower: Int, sugar: Int, eggs: Int, milk: Int, chocolate: Int) {
self.flower = flower
self.sugar = sugar
self.eggs = eggs
self.milk = milk
self.chocolate = chocolate
}
// init for creating an almond cake:
init(flower: Int, sugar: Int, yeast: Int, almonds: Int, eggs: Int, salt: Int) {
self.flower = flower
self.sugar = sugar
self.yeast = yeast
self.almonds = almonds
self.eggs = eggs
self.salt = salt
}
}
enum Ingredient {
case chocolate
case almond
func bake(recipe: Recipe) -> Cake? {
switch self {
case chocolate:
print("chocolate")
if recipe.yeast > 0 || recipe.almonds > 0 || recipe.salt > 0 {
return nil
// or maybe a fatal error!!
}
// return a Chocolate Cake based on the given recipe:
case almond:
print("almond")
if recipe.chocolate > 0 {
return nil
// or maybe a fatal error!!
}
// return an Almond Cake based on the given recipe:
}
}
}
Usage:
// bake chocolate cake with a custom recipe
let bakedChocolateCake = Ingredient.chocolate.bake(Recipe(flower: 500, sugar: 300, eggs: 3, milk: 200, chocolate: 200)
// bake almond cake with a custom recipe
let bakedAlmondCake = Ingredient.chocolate.bake(Recipe(flower: 300, sugar: 200, yeast: 20, almonds: 200, eggs: 5, salt: 2))
Even if those are not the optimal solution for your case, I hope it helped.

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.

Can a Rust macro create new identifiers?

I'd like to create a setter/getter pair of functions where the names are automatically generated based on a shared component, but I couldn't find any example of macro rules generating a new name.
Is there a way to generate code like fn get_$iden() and SomeEnum::XX_GET_$enum_iden?
If you are using Rust >= 1.31.0 I would recommend using my paste crate which provides a stable way to create concatenated identifiers in a macro.
macro_rules! make_a_struct_and_getters {
($name:ident { $($field:ident),* }) => {
// Define the struct. This expands to:
//
// pub struct S {
// a: String,
// b: String,
// c: String,
// }
pub struct $name {
$(
$field: String,
)*
}
paste::item! {
// An impl block with getters. Stuff in [<...>] is concatenated
// together as one identifier. This expands to:
//
// impl S {
// pub fn get_a(&self) -> &str { &self.a }
// pub fn get_b(&self) -> &str { &self.b }
// pub fn get_c(&self) -> &str { &self.c }
// }
impl $name {
$(
pub fn [<get_ $field>](&self) -> &str {
&self.$field
}
)*
}
}
};
}
make_a_struct_and_getters!(S { a, b, c });
My mashup crate provides a stable way to create new identifiers that works with any Rust version >= 1.15.0.
#[macro_use]
extern crate mashup;
macro_rules! make_a_struct_and_getters {
($name:ident { $($field:ident),* }) => {
// Define the struct. This expands to:
//
// pub struct S {
// a: String,
// b: String,
// c: String,
// }
pub struct $name {
$(
$field: String,
)*
}
// Use mashup to define a substitution macro `m!` that replaces every
// occurrence of the tokens `"get" $field` in its input with the
// concatenated identifier `get_ $field`.
mashup! {
$(
m["get" $field] = get_ $field;
)*
}
// Invoke the substitution macro to build an impl block with getters.
// This expands to:
//
// impl S {
// pub fn get_a(&self) -> &str { &self.a }
// pub fn get_b(&self) -> &str { &self.b }
// pub fn get_c(&self) -> &str { &self.c }
// }
m! {
impl $name {
$(
pub fn "get" $field(&self) -> &str {
&self.$field
}
)*
}
}
}
}
make_a_struct_and_getters!(S { a, b, c });
No, not as of Rust 1.22.
If you can use nightly builds...
Yes: concat_idents!(get_, $iden) and such will allow you to create a new identifier.
But no: the parser doesn't allow macro calls everywhere, so many of the places you might have sought to do this won't work. In such cases, you are sadly on your own. fn concat_idents!(get_, $iden)(…) { … }, for example, won't work.
There's a little known crate gensym that can generate unique UUID names and pass them as the first argument to a macro, followed by a comma:
macro_rules! gen_fn {
($a:ty, $b:ty) => {
gensym::gensym!{ _gen_fn!{ $a, $b } }
};
}
macro_rules! _gen_fn {
($gensym:ident, $a:ty, $b:ty) => {
fn $gensym(a: $a, b: $b) {
unimplemented!()
}
};
}
mod test {
gen_fn!{ u64, u64 }
gen_fn!{ u64, u64 }
}
If all you need is a unique name, and you don't care what it is, that can be useful. I used it to solve a problem where each invocation of a macro needed to create a unique static to hold a singleton struct. I couldn't use paste, since I didn't have unique identifiers I could paste together in the first place.