Rust / Yew Geolocation - rust

I'm trying to retrieve the user's lat/lon, once, in the create function of a Yew Component, so that I can do some math and pass useful decisions to child components. I've made the Yew hook "use_geolocation()" work just fine, but that only runs in a function_component, and there doesn't appear to be any straightforward way to use that location in other components.
Then I found this neat tutorial which uses wasm_bindgen and Seed::app::orders::Orders to make a "cheap clone" of the app, and to call the Javascript functions. The bindings look like:
#[wasm_bindgen]
extern "C" {
type GeolocationCoordinates;
#[wasm_bindgen(method, getter)]
fn latitude(this: &GeolocationCoordinates) -> f64;
#[wasm_bindgen(method, getter)]
fn longitude(this: &GeolocationCoordinates) -> f64;
type GeolocationPosition;
#[wasm_bindgen(method, getter)]
fn coords(this: &GeolocationPosition) ->
GeolocationCoordinates;
}
And the function for fetching geolocation, splicing together the chunks of code from Tor Hovland's tutorial:
let (app, msg_mapper) = (orders.clone_app(), orders.msg_mapper());
let geo_callback = move |position: JsValue| {
let pos: GeolocationPosition = position.into();
let coords = pos.coords();
app.update(msg_mapper(Msg::Position(
coords.latitude(),
coords.longitude(),
)));
};
let geolocation = web_sys::window()
.expect("Unable to get browser window.")
.navigator()
.geolocation()
.expect("Unable to get geolocation.");
let geo_callback_function =
Closure::wrap(
Box::new(|pos| geo_callback(pos)) as Box<dyn Fn(JsValue)>
);
geolocation.get_current_position(
&geo_callback_function.as_ref().unchecked_ref()
).expect("Unable to get position");
geo_callback_function.forget();
I attempted this route, but found that adding the line seed = "0.9.1" into my Cargo.toml produced compile errors, something about a type mismatch between a closure in wasm_bindgen and something in seed. Included here for completeness:
error[E0283]: type annotations needed for `Closure<T>`
--> /home/djmcmath/.cargo/registry/src/github.com-1ecc6299db9ec823/seed-
0.9.1/src/browser/service/routing.rs:87:9
|
87 | let closure = Closure::new(move |event: web_sys::Event| {
| ^^^^^^^
|
= note: cannot satisfy `_: WasmClosure`
note: required by a bound in `Closure::<T>::new`
--> /home/djmcmath/.cargo/registry/src/github.com-1ecc6299db9ec823/wasm-bindgen-
0.2.81/src/closure.rs:251:17
|
251 | T: ?Sized + WasmClosure,
| ^^^^^^^^^^^ required by this bound in `Closure::<T>::new`
help: consider giving `closure` an explicit type, where the type for type parameter
`T` is specified
|
87 | let closure: Closure<T> = Closure::new(move |event: web_sys::Event| {
| ++++++++++++
help: consider specifying the type argument in the function call
|
87 | let closure = Closure::new::<F>(move |event: web_sys::Event| {
| +++++
After beating my head against that brick wall for a while, I decided to just not use Seed, but I don't know of another way to make a "cheap clone" of the app to make the lifetimes work out correctly. Without that, I get the predictable lifetime error on the geo_callback_function:
let geo_callback_function =
Closure::wrap(
Box::new(|pos: JsValue| geo_callback(pos)) as Box<dyn Fn(JsValue)>
);
Error message:
error[E0597]: `geo_callback` does not live long enough
--> src/ferry_route.rs:213:37
|
213 | Box::new(|pos: JsValue| geo_callback(pos)) as Box<dyn Fn(JsValue)>
| ------------------------^^^^^^^^^^^^------
| | | |
| | | borrowed value does not live long enough
| | value captured here
| cast requires that `geo_callback` is borrowed for `'static`
...
221 | }
| - `geo_callback` dropped here while still borrowed
So I'm at a loss, at this point. Seems like fetching user's location would be simpler than all of this. I'm open to any path that makes any of these work. (Definition of work: I can get the user's lat/lon in the create function of a Yew Component, so that I can do some math and pass useful decisions to child components.)

Ok, I think I've got it. Or at least, I've got something that works. I'm not sure it's the most elegant solution. Basically, drop the Seed parts out of the function and just pull the coords from the JsValue, like so:
fn geo_callback(position: JsValue) {
let pos = JsCast::unchecked_into::<GeolocationPosition>(position);
let coords = pos.coords();
log::info!(
"Latitude: {}. Longitude: {}.",
coords.latitude(),
coords.longitude()
);
};
At this point, I'm pretty sure I can take those coords and do something useful with them.

Related

implementing obj-rs example to glium

I am trying to load a model into glium, I am using crate obj-rs for this. My model works fine with this example. Unfortunately whenever I try to move code from the example to my project (which is basically code copied from here) I fail with an error:
error[E0277]: the trait bound `glium::Display: glium::backend::Facade` is not satisfied
--> src/main.rs:27:32
|
27 | let vb = obj.vertex_buffer(&display)?;
| ------------- ^^^^^^^^ the trait `glium::backend::Facade` is not implemented for `glium::Display`
| |
| required by a bound introduced by this call
|
= help: the trait `glium::backend::Facade` is implemented for `Rc<glium::context::Context>`
note: required by a bound in `obj::glium_support::<impl Obj<V, I>>::vertex_buffer`
--> /Users/user/project/obj-rs/src/lib.rs:309:33
|
309 | pub fn vertex_buffer<F: Facade>(
| ^^^^^^ required by this bound in `obj::glium_support::<impl Obj<V, I>>::vertex_buffer`
error[E0277]: the trait bound `glium::Display: glium::backend::Facade` is not satisfied
--> src/main.rs:28:31
|
28 | let ib = obj.index_buffer(&display)?;
| ------------ ^^^^^^^^ the trait `glium::backend::Facade` is not implemented for `glium::Display`
| |
| required by a bound introduced by this call
|
= help: the trait `glium::backend::Facade` is implemented for `Rc<glium::context::Context>`
note: required by a bound in `obj::glium_support::<impl Obj<V, I>>::index_buffer`
--> /Users/user/project/obj-rs/src/lib.rs:317:32
|
317 | pub fn index_buffer<F: Facade>(
| ^^^^^^ required by this bound in `obj::glium_support::<impl Obj<V, I>>::index_buffer`
I am new to rust so most of the time I don't know what I am doing. I find it really hard to simply make copied code work. I tried implementing Facade for Display but that didn't seem to make any difference, just more errors. Could you tell me what's wrong, or at least provide me with an example that works, I just want to add my own models to glium examples.
Here are the parts of code that I changed:
fn main() -> Result<(), Box<dyn std::error::Error>>{
#[allow(unused_imports)]
use glium::{glutin, Surface};
use obj::{load_obj, Obj};
use glium::glutin::dpi::LogicalSize;
let event_loop = glutin::event_loop::EventLoop::new();
let window = glutin::window::WindowBuilder::new()
.with_inner_size(LogicalSize::new(500.0, 400.0))
.with_title("obj-rs");
let context = glutin::ContextBuilder::new().with_depth_buffer(24);
let display = glium::Display::new(window, context, &event_loop)?;
let input = include_bytes!("model.obj");
let obj: Obj = load_obj(&input[..])?;
//two lines causing the problem
//let vb = obj.vertex_buffer(&display)?;
//let ib = obj.index_buffer(&display)?;
let positions = glium::VertexBuffer::new(&display, &teapot::VERTICES).unwrap();
..... rest is the same as in the glium example
One thing that I would like to differ from obj-rs example is not to use local source if you know what I mean. Basically something like this:
[dependencies]
glium = "*"
obj-rs = { version = "*", features = ["glium"] }

Convert for loop to iterator in rust

So, I am trying to build a vector of vectors of a custom type.I am trying to implement a default for it and was successful with a for loop.
let mut data: Vec<Vec<Cell>> = Vec::new();
for _i in 0..63 {
let mut row: Vec<Cell> = Vec::with_capacity(64);
for _j in 0..63 {
row.push(Cell::default())
}
data.push(row);
}
I felt this code could do with some functional style and interators, so I decided to do something like so:
let data: Vec<Vec<Cell>> = Vec::with_capacity(64)
.iter_mut()
.map(|mut x: &mut Vec<Cell>| {
x = Vec::with_capacity(64)
.iter_mut()
.map(|mut y: Cell| y = Cell::default())
.collect()
})
.collect();
With this, I get an error like so:
error[E0631]: type mismatch in closure arguments
--> src/types.rs:124:26
|
124 | .map(|mut y: Cell| y = Cell::default())
| ^^^ ------------- found signature defined here
| |
| expected due to this
|
= note: expected closure signature `fn(&mut _) -> _`
found closure signature `fn(types::cell::Cell) -> _`
note: required by a bound in `map`
--> /home/naitik/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/iter/traits/iterator.rs:779:12
|
779 | F: FnMut(Self::Item) -> B,
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `map`
I do not understand the problem here. What exactly is it? What can I do to fix it?
You're misunderstanding how Vec::with_capacity works. It doesn't actually put anything into a vec that you can iterate over and assign to. You want to map Cell::default over something and then collect that into a vec. That something doesn't matter, only that there's 64 of them, so a range will do:
let data: Vec<Vec<Cell>> = (0..64)
.map(|_| (0..64).map(|_| Cell::default()).collect())
.collect();
collect should hopefully figure out the needed capacities from the ranges' TrustedLen impl on its own to avoid unnecessary re-allocations.
I would, however, doubt that this is better than the procedural approach. It seems less clear and harder to modify to me. Just stick to good ol' for loops is my two cents.
If Cell is Clone or Copy you could even just do:
let data: Vec<Vec<Cell>> = vec![vec![Cell::default(); 64]; 64];
for maximal minimality.

Embedded Rust Discovery Book example fails to compile

I am new to rust and embedded systems. I was reading and going along "The Discovery Book" and I can't seem to figure out the errors below.
I have placed this in my main.rs:
#![deny(unsafe_code)]
#![no_main]
#![no_std]
use aux5::{entry, prelude::*, Delay, Leds};
#[entry]
fn main() -> ! {
let (mut delay, mut leds): (Delay, Leds) = aux5::init();
let half_period = 500_u16;
loop {
leds[0].on();
delay.delay_ms(half_period);
leds[0].off();
delay.delay_ms(half_period);
}
}
My auxiliary lib.rs contains:
pub use stm32f3_discovery::{leds::Leds, stm32f3xx_hal, switch_hal};
pub use switch_hal::{ActiveHigh, OutputSwitch, Switch, ToggleableOutputSwitch};
use stm32f3xx_hal::prelude::*;
pub use stm32f3xx_hal::{
delay::Delay,
gpio::{gpioe, Output, PushPull},
hal::blocking::delay::DelayMs,
stm32,
};
pub type LedArray = [Switch<gpioe::PEx<Output<PushPull>>, ActiveHigh>; 8];
pub fn init() -> (Delay, LedArray) {
let device_periphs = stm32::Peripherals::take().unwrap();
let mut reset_and_clock_control = device_periphs.RCC.constrain();
let core_periphs = cortex_m::Peripherals::take().unwrap();
let mut flash = device_periphs.FLASH.constrain();
let clocks = reset_and_clock_control.cfgr.freeze(&mut flash.acr);
let delay = Delay::new(core_periphs.SYST, clocks);
// initialize user leds
let mut gpioe = device_periphs.GPIOE.split(&mut reset_and_clock_control.ahb);
let leds = Leds::new(
gpioe.pe8,
gpioe.pe9,
gpioe.pe10,
gpioe.pe11,
gpioe.pe12,
gpioe.pe13,
gpioe.pe14,
gpioe.pe15,
&mut gpioe.moder,
&mut gpioe.otyper,
);
(delay, leds.into_array())
}
I have not changed anything in lib.rs and I am following the instructions from the book, along with the source code.
Running the code results in the following errors when building for the target:
error[E0432]: unresolved import `aux5::prelude`
--> src\05-led-roulette\src\main.rs:5:19
|
5 | use aux5::{entry, prelude::*, Delay, Leds};
| ^^^^^^^ could not find `prelude` in `aux5`
error[E0308]: mismatched types
--> src\05-led-roulette\src\main.rs:9:48
|
9 | let (mut delay, mut leds): (Delay, Leds) = aux5::init();
| ------------- ^^^^^^^^^^^^ expected struct `Leds`, found array of 8 elements
| |
| expected due to this
|
= note: expected tuple `(Delay, Leds)`
found tuple `(Delay, [Switch<PEx<Output<PushPull>>, ActiveHigh>; 8])`
error[E0608]: cannot index into a value of type `Leds`
--> src\05-led-roulette\src\main.rs:14:9
|
14 | leds[0].on();
| ^^^^^^^
error[E0599]: no method named `delay_ms` found for struct `Delay` in the current scope
--> src\05-led-roulette\src\main.rs:15:15
|
15 | delay.delay_ms(half_period);
| ^^^^^^^^ method not found in `Delay`
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
5 | use aux5::DelayMs;
|
error[E0608]: cannot index into a value of type `Leds`
--> src\05-led-roulette\src\main.rs:17:9
|
17 | leds[0].off();
| ^^^^^^^
error[E0599]: no method named `delay_ms` found for struct `Delay` in the current scope
--> src\05-led-roulette\src\main.rs:18:15
|
18 | delay.delay_ms(half_period);
| ^^^^^^^^ method not found in `Delay`
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope; perhaps add a `use` for it:
|
5 | use aux5::DelayMs;
|
error: aborting due to 6 previous errors
Some errors have detailed explanations: E0308, E0432, E0599, E0608.
For more information about an error, try `rustc --explain E0308`.
error: could not compile `led-roulette`
To learn more, run the command again with --verbose.
From the following page, "The Led and Delay abstractions", it looks like this is the set of imports that you need.
use aux5::{entry, Delay, DelayMs, LedArray, OutputSwitch};
In the above abstraction, compared to your main.rs, note the:
Addition of DelayMs trait, making .delay_ms() available.
Removal of a prelude as it is not being used.
Change from Leds to the expected LedArray.
Also see:
Stack Overflow, "Why do I need to import a trait to use the methods it defines for a type?"

error[E0597]: borrowed value does not live long enough in While loop

I am really new to Rust, I am having trouble solving this error, but it only happens if I comment out the while statement , basicly I am asking values from the console and storing it in a HashMap:
use std::collections::HashMap;
use std::io;
fn main() {
let mut customers = HashMap::new();
let mut next_customer = true;
while next_customer {
let mut input_string = String::new();
let mut temp_vec = Vec::with_capacity(3);
let mut vec = Vec::with_capacity(2);
println!("Insert new customer f.e = customer id,name,address:");
io::stdin().read_line(&mut input_string);
input_string = input_string.trim().to_string();
for s in input_string.split(",") {
temp_vec.push(s);
}
vec.push(temp_vec[1]);
vec.push(temp_vec[2]);
let mut key_value = temp_vec[0].parse::<i32>().unwrap();
customers.insert(key_value, vec);
next_customer = false;
}
println!("DONE");
}
The code results in the error
error[E0597]: `input_string` does not live long enough
--> src/main.rs:14:18
|
14 | for s in input_string.split(",") {
| ^^^^^^^^^^^^ borrowed value does not live long enough
...
20 | customers.insert(key_value, vec);
| --------- borrow later used here
21 | next_customer = false;
22 | }
| - `input_string` dropped here while still borrowed
As others have said the problem lies with the lifetime and/or type of the values getting put into the customers map.
customers.insert(key_value, vec);
| --------- borrow later used here
Often this happens when the compiler has decided to give an object a type that you didn't expect. To find out what it's doing you can force the type, and see how it complains. Changing the code to:
let mut customers: HashMap<(),()> = HashMap::new();
Gives us two relevant errors:
20 | customers.insert(key_value, vec);
| ^^^^^^^^^ expected `()`, found `i32`
...
20 | customers.insert(key_value, vec);
| ^^^ expected `()`, found struct `std::vec::Vec`
|
= note: expected unit type `()`
found struct `std::vec::Vec<&str>`
So the type that the compiler wants to give our customers object is HashMap<i32, Vec<&str>>
The problem is that the &str lifetime has got to be inside the block as we don't store the Strings anywhere, and they can't have 'static lifetime since they're user input.
This means we probably want a HashMap<i32,Vec<String>>.
Changing the code to use one of those gives us an error about vec not having the right type: It's getting deduced as a Vec<&str>, but we want a Vec<String>.
We have two options.
Convert the vec to the right type just before we insert it into the map using customers.insert(key_value, vec.iter().map(|s| s.to_string()).collect()). (Though you may want to extract it to a variable for clarity).
Explicitly change the type of vec to Vec<String>
Option 1 "just works". While option 2 leads us down a path of making similar changes closer and closer to the read_line call.
Once you've decided on the fix in option 1, you can remove the manual type annotations that were added to work out the fix, if you find them overly noisy.
The issue is that you are passing around reference to underlying &str values that will get dropped. One way is to take the input string, trim and split it, then clone it going into the other vector.
let temp_vec: Vec<String> = input_string.trim().split(",").map(|t| t.to_string()).collect();
vec.push(temp_vec[1].clone());
vec.push(temp_vec[2].clone());

Why does `&value.into_something()` still result in a moved value?

I'm struggling to see how this transfers ownership. Here is my code:
let res = screenshot::take_screenshot(0);
let file = File::open("test.png").expect("Failed to open file");
let encoder = PNGEncoder::new(file);
encoder.encode(&res.into_raw(),
res.width(),
res.height(),
ColorType::RGBA(0)
);
screenshot::take_screenshot is a function that returns an ImageBuffer<Rgba<u8>, Vec<u8>>. Here is the compiler error I'm getting:
error[E0382]: use of moved value: `res`
--> src/main.rs:21:37
|
21 | encoder.encode(&res.into_raw(), res.width(), res.height(), ColorType::RGBA(0));
| --- ^^^ value used here after move
| |
| value moved here
|
= note: move occurs because `res` has type `image::ImageBuffer<image::Rgba<u8>, std::vec::Vec<u8>>`, which does not implement the `Copy` trait
error[E0382]: use of moved value: `res`
--> src/main.rs:21:50
|
21 | encoder.encode(&res.into_raw(), res.width(), res.height(), ColorType::RGBA(0));
| --- value moved here ^^^ value used here after move
|
= note: move occurs because `res` has type `image::ImageBuffer<image::Rgba<u8>, std::vec::Vec<u8>>`, which does not implement the `Copy` trait
I am trying to pass a slice, which I believe is a reference of the vector, is it not? This would imply ownership is not passed, and the vector isn't moved. I know I'm doing something wrong and it's likely something simple.
This is simply an operator precedence issue: methods apply before the reference operator &:
&(res.into_raw()) // This
(&res).into_raw() // Not this
Calling into_raw takes ownership and the value is gone.
You could do something like this:
let w = res.width();
let h = res.height();
let r = res.into_raw();
encoder.encode(&r, w, h, ColorType::RGBA(0));
It's likely there's something nicer, but you haven't provided a MCVE so it's hard to iterate on a solution. Blindly guessing from the docs, it looks like this should work:
extern crate image;
use image::{png::PNGEncoder, ColorType, ImageBuffer, Rgba};
use std::io;
fn repro<W: io::Write>(res: ImageBuffer<Rgba<u8>, Vec<u8>>, file: W) -> Result<(), io::Error> {
let encoder = PNGEncoder::new(file);
encoder.encode(&res, res.width(), res.height(), ColorType::RGBA(0))
}
fn main() {}

Resources