Hello
I'm getting the following error at runtime after pressing the =-button:
thread 'main' panicked at 'StringComponentStore.get_mut: internal downcast error', /Users/lol/.cargo/git/checkouts/dces-rust-0e90bfa1cf1cf6e4/fb2c263/src/component/string_component_store.rs:249:21:
With the following code:
use orbtk::*;
/// Module holding all the mathematical logic to actually calculate a result
mod logic {
use std::str::FromStr;
// Trait to allow subscription access to chars in Strings using ch function.
trait StringSubscription {
fn ch(&self, pos: usize) -> char;
}
impl StringSubscription for String {
fn ch(&self, pos: usize) -> char {
self.chars().nth(pos).unwrap()
}
}
/**
* Function to check if given character is an operator or not.
*
* # Arguments
* * `c` - Character to check.
*
* # Return
* Boolean. Returns true if `c` is an operator.
*
* # Example
* ```
* assert_eq!(is_operator('+'), true);
* assert_eq!(is_operator('x'), true);
* ```
*/
fn is_operator(c: char) -> bool {
let operators : [char; 4] = ['+', '-', '*', '/'];
if operators.contains(&c) {
true
} else {
false
}
}
/**
* Function to get precedence of an operator. This to determine
* which operator to evaluate first.
*
* # Arguments
* * `operator` - Char. Operator to check
*
* # Return
* i8. Returns an integer representing the precedence of the `operator`.
*
* # Example
* ```
* assert_eq!(precedence('+') < precedence('*'), true);
* assert_eq!(precedence('*') == precedence('/'), true);
* assert_eq!(precedence('-') >= precedence('/'), false);
* ```
*/
fn precedence(operator: char) -> i8 {
match operator {
'+' => 1,
'-' => 1,
'*' => 2,
'/' => 2,
_ => -1,
}
}
/**
* Function to convert an infix string to postfix notation.
*
* # Arguments
* * `infix` - String containing the infix.
*
* # Return
* Returns a string in postfix notation.
*
* # Example
* ```
* let s : String = "5+5";
* assert_eq!(to_postfix(s), "5 5 +");
* ```
*
*/
fn to_postfix(mut infix: String) -> String {
let mut postfix : String = "".to_string();
// Stack to temporarily hold operators, and nr is
// a helper variable to group digits together in their number
let mut stack : std::vec::Vec<char> = std::vec::Vec::new();
let mut nr : String = "".to_string();
// If first character in infix is a minus-operator (AKA first number is a negative)
// Add "0" to create "0-[number]"
if infix.ch(0) == '-' {
infix = format!("{}{}", "0", infix);
}
// Looping over infix string
for mut i in 0..infix.len() {
// If currently evaluated character ain't an operator, it's a digit
if !is_operator(infix.ch(i)) {
// If digit is first one in a group of digits (AKA a number)
// put that number in nr
while !is_operator(infix.ch(i)) && i < infix.len() {
nr = format!("{}{}", nr, infix.chars().nth(i).unwrap());
i = i + 1;
}
i = i - 1;
// Append number to postfix string
postfix = format!("{}{}", postfix, nr);
nr = "".to_string();
} else {
// This block is executed when the evaluated character is an operator
// If the stack is empty, or the evaluated operator has a higher precedence than the
// one in the stack, push it (Needs to be appended to the postfix string later)
if stack.is_empty() || precedence(infix.ch(i)) > precedence(*stack.last().unwrap()) {
stack.push(infix.ch(i));
} else {
// While the stack contains a higher or equally high precedence as the
// evaluated character: append top of stack to postfix string
while precedence(*stack.last().unwrap()) >= precedence(infix.ch(i)) {
postfix = format!("{}{}{}", postfix, stack.pop().unwrap(), ' ');
if stack.is_empty() {
break;
}
}
// Push evaluated operator to stack
stack.push(infix.ch(i));
}
}
}
// Append all the remaining operators from stack to postfix string
while !stack.is_empty() {
postfix = format!("{}{}", postfix, stack.pop().unwrap());
}
postfix
}
/**
* Evaluate two numbers regarding operator
*
* # Arguments
* * `x` - First number to do evaluation witch
* * `y` - Second number to do evaluation with
* * `operator` - Operator (+, -, *, /) to evaluate `x` and `y` with
*
* # Return
* Returns the result of the evaluation
*
* # Example
* ```
* assert_eq!(evaluate(5, 2, '+'), 7);
* assert_eq!(evaluate(2, 2, '*'), 4);
* assert_eq!(evaluate(5, 10, '-'), -5);
* assert_eq!(evaluate(0, -5, '-'), 5);
* ```
*
*/
fn evaluate(x: f64, y: f64, operator: char) -> f64 {
match operator {
'+' => x + y,
'-' => x - y,
'*' => x * y,
'/' => x / y,
_ => 0.00
}
}
/**
* Calculate the result of an infix string (`s` gets converted to postfix inside this
* function)
*
*
* # Return
* Returns the result as a double.
*
* # Example
* ```
* assert_eq!(calculate("5+5"), 10);
* assert_eq!(calculate("5+5*2"), 15);
* assert_eq!(calculate("5-20/2"), -5);
* ```
*/
pub fn calculate(s: &String) -> f64 {
// Convert to postfix
let s = to_postfix(s.clone());
// Stack for holding operators and nr for grouping digits who belong together as a number
let mut stack : std::vec::Vec<f64> = std::vec::Vec::new();
let mut nr : String = "".to_string();
for mut i in 0..s.len() {
if s.ch(i) == ' ' {
continue;
}
// If evaluated character is a digit, put it in nr
if s.ch(i).is_digit(10) {
// If digit is first in a group of digits (AKA a number), put that
// whole number in nr
while s.ch(i).is_digit(10) {
nr = format!("{}{}", nr, s.ch(i));
i = i + 1;
}
i = i - 1;
// Pushing nr to stack
stack.push(f64::from_str(&nr).unwrap());
nr = "".to_string();
} else {
// If current evaluated character is not a digit
// but an operator, do a calculation
// Retrieve first number of calculation
let x : f64 = stack.pop().unwrap();
let y : f64 = stack.pop().unwrap();
// Put evaluation result in integer and push into stack
let result : f64 = evaluate(x, y, s.ch(i));
stack.push(result);
}
}
// Final number is in stack
*stack.last().unwrap()
}
}
extern crate strum;
#[macro_use]
extern crate strum_macros;
widget!(MainView<MainViewState>);
// Enumeration of all possible operators
#[derive(EnumString)]
enum Operator {
#[strum(serialize="+")]
Plus,
#[strum(serialize="-")]
Min,
#[strum(serialize="*")]
Times,
#[strum(serialize="/")]
Divide,
}
#[derive(Copy, Clone)]
enum Action {
Char(char),
}
#[derive(AsAny, Default)]
pub struct MainViewState {
screen: String,
action: Option<Action>,
}
impl MainViewState {
fn action(&mut self, action: impl Into<Option<Action>>) { //?
self.action = action.into();
}
}
impl State for MainViewState {
fn update(&mut self, _: &mut Registry, ctx: &mut Context) {
if let Some(action) = self.action {
match action {
Action::Char(c) => match c {
'=' => {
let mut screen = ctx.child("screen");
let screen_text : &String = screen.get_mut::<String>("text");
let result : f64 = logic::calculate(screen_text);
}
_ => {
ctx.child("screen").get_mut::<String16>("text").push(c);
}
}
}
self.action = None;
}
}
}
// helper to request MainViewState
fn state<'a>(id: Entity, states: &'a mut StatesContext) -> &'a mut MainViewState {
states.get_mut(id)
}
impl Template for MainView {
fn template(self, id: Entity, ctx: &mut BuildContext) -> Self {
// Initializing Grid
let mut grid = Grid::create();
// Configuring grid (amount of rows and columns)
grid = grid
.columns(
Columns::create()
.column("*")
.column("*")
.column("*")
.column("*")
.build()
)
.rows(
Rows::create()
.row(50.0)
.row(50.0)
.row(50.0)
.row(50.0)
.row(50.0)
.row(50.0)
.row(50.0)
.build()
);
//Adding textbox holding entered numbers and operators
grid = grid.child(
TextBox::create()
.text("")
.id("screen")
.attach(Grid::row(0))
.attach(Grid::column(0))
.attach(Grid::column_span(4))
.build(ctx)
);
// Adding all buttons from 1-9 to the grid
// in calculator format
let mut counter : u8 = 9;
for i in 1..4 {
for j in 0..3 {
grid = grid.child(
Button::create()
.text(counter.to_string())
.attach(Grid::column(2-j))
.attach(Grid::row(i))
.on_click({
move |states, _| -> bool {
state(id, states).action(Action::Char(std::char::from_digit(counter as u32, 10).unwrap()));
true
}
})
.build(ctx)
);
counter = counter - 1;
}
}
// Adding +, -, x, /
let operators : Vec<char> = vec!['+', '-', '*', '/', '='];
let mut i = 1;
for operator in operators {
grid = grid.child(
Button::create()
.text(operator.to_string())
.attach(Grid::column(3))
.attach(Grid::row(i))
.on_click({
move |states, _| -> bool {
state(id, states).action(Action::Char(operator));
true
}
})
.build(ctx)
);
i = i + 1;
}
// Adding zero-button (Seperate because of special column-span)
grid = grid.child(
Button::create()
.text("0")
.attach(Grid::column(0))
.attach(Grid::row(4))
.attach(Grid::column_span(3))
.on_click({
move |states, _| -> bool {
state(id, states).action(Action::Char(std::char::from_digit(0, 10).unwrap()));
true
}
}).build(ctx)
);
self.name("MainView").child(
grid.build(ctx)
)
}
}
fn main() {
Application::new()
.window(|ctx| {
Window::create()
.title("OrbTk")
.position((100.0, 100.0))
.size(212.0, 336.0)
.child(MainView::create().build(ctx))
.build(ctx)
})
.run();
}
Cargo.toml:
[package]
name = "calculator"
version = "0.1.0"
authors = ["lol"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
orbtk = { git = "https://github.com/redox-os/orbtk.git", branch = "develop" }
strum = "0.18.0"
strum_macros = "0.18.0"
I think it gets caused by one of the following lines: L86, L198, L201, L290-294.
Google didn't gave any useful hits for this error though...