I am having problem getting a reference out of a RefCell<Option<Rc>>.
Any suggestion?
struct Node<T> {
value: T
}
struct Consumer3<T> {
tail: RefCell<Option<Rc<Node<T>>>>,
}
impl<T> Consumer3<T> {
fn read<'s>(&'s self) -> Ref<Option<T>> {
Ref::map(self.tail.borrow(), |f| {
f.map(|s| {
let v = s.as_ref();
v.value
})
})
}
}
Gives:
error[E0308]: mismatched types
--> src/lib.rs:15:13
|
15 | / f.map(|s| {
16 | | let v = s.as_ref();
17 | | v.value
18 | | })
| |______________^ expected reference, found enum `Option`
|
= note: expected reference `&_`
found enum `Option<T>`
help: consider borrowing here
|
15 | &f.map(|s| {
16 | let v = s.as_ref();
17 | v.value
18 | })
|
error: aborting due to previous error
Playground
Mapping from one Ref to another requires that the target already exist in memory somewhere. So you can't get a Ref<Option<T>> from a RefCell<Option<Rc<Node<T>>>>, because there's no Option<T> anywhere in memory.
However, if the Option is Some, then there will be a T in memory from which you can obtain a Ref<T>; if the Option is None, obviously you can't. So returning Option<Ref<T>> may be a viable alternative for you:
use std::{cell::{Ref, RefCell}, rc::Rc};
struct Node<T> {
value: T
}
struct Consumer3<T> {
tail: RefCell<Option<Rc<Node<T>>>>,
}
impl<T> Consumer3<T> {
fn read(&self) -> Option<Ref<T>> {
let tail = self.tail.borrow();
if tail.is_some() {
Some(Ref::map(tail, |tail| {
let node = tail.as_deref().unwrap();
&node.value
}))
} else {
None
}
}
}
Playground.
Related
In Rust is it possible to update a struct from a thread started in one of the structs member functions?
I have an example below and the error I am getting is that you can't use self as a variable name.
use std::time::Duration;
use glib::{clone, Continue, MainContext, PRIORITY_DEFAULT};
use adw::{Application, ApplicationWindow};
use adw::prelude::*;
use std::thread;
const APP_ID: &str = "org.struct_threads";
fn main() {
let app = Application::builder().application_id(APP_ID).build();
app.connect_activate(build_ui);
app.run();
}
pub fn build_ui(app: &Application) {
let window = ApplicationWindow::builder()
.application(app)
.build();
let window_clone = window.clone();
let astruct = aStruct { aString : String::new(), aBool : false };
astruct.update_string();
while true {
println!("aString = {}", astruct.aString);
};
}
struct aStruct {
aString : String,
aBool : bool
}
impl aStruct {
pub fn update_string(&mut self) {
let (sender, receiver) = MainContext::channel(PRIORITY_DEFAULT);
thread::spawn(move || {
loop {
//let thisString = "";
if self.aString == "Value two" {
sender.send("Value one").expect("Could not send through channel");
//thisString = "Value two";
}
else {
sender.send("Value two").expect("Could not send through channel");
//thisString = "Value one";
}
//self.aStinrg = thisString.to_string();
thread::sleep(Duration::from_secs(10));
};
});
receiver.attach(
None,
clone!(#weak self => #default-return Continue(false),
move |reciever_string| {
self.aString = reciever_string;
Continue(true)
}
),
);
}
}
Error:
error: proc macro panicked
--> src/main.rs:99:13
|
99 | / clone!(#weak self => #default-return Continue(false),
100 | | move |reciever_string| {
101 | | self.aString = reciever_string;
102 | | Continue(true)
103 | | }
104 | | ),
| |____________^
|
= help: message: Can't use `self` as variable name. Try storing it in a temporary variable or rename it using `as`.
If I clone self and pass a normal variable name into the receiver I get an error stating that the struct does not implement Downgrade which doesn't seem to be implementable for booleans.
I get the same Downgrade error if I try and move this block into a non member function of the struct and call it separately.
Downgrade error:
error[E0277]: the trait bound `aStruct: Downgrade` is not satisfied
--> src/main.rs:99:13
|
99 | / clone!(#weak self_clone => #default-return Continue(false),
100 | | move |reciever_string| {
101 | | self.aString = reciever_string.to_string();
102 | | Continue(true)
103 | | }
104 | | ),
| |____________^ the trait `Downgrade` is not implemented for `aStruct`
|
= help: the following other types implement trait `Downgrade`:
&T
ATContext
AboutDialog
AboutWindow
Accessible
Action
ActionBar
ActionGroup
and 493 others
= note: required for `&aStruct` to implement `Downgrade`
= note: this error originates in the macro `clone` (in Nightly builds, run with -Z macro-backtrace for more info)
Finally if I just try and update the struct from within the thread using either self or a copy I get an error stating that the value does not live long enough. Is there way to update a struct from a thread?
I'm trying to do this, but it doesn't work:
struct Pack {
data: Box<[u8]>
}
fn foo() {
let mut p = Pack {
data: Box::new(**b"Hello!")
};
p.data.set(b"Bye!");
}
I'm getting:
error[E0614]: type `[u8; 6]` cannot be dereferenced
--> foo
|
30 | data: Box::new(**b"Hello!")
| ^^^^^^^^^^^
error[E0277]: the size for values of type `[u8]` cannot be known at compilation time
--> foo
|
30 | data: Box::new(**b"Hello!")
| ^^^^^^^^ doesn't have a size known at compile-time
|
What is the right way to do this?
Just use a Vec instead:
struct Pack {
data: Vec<u8>
}
fn foo() {
let mut p = Pack {
data: b"Hello!".to_vec()
};
p.data=b"Bye!".to_vec();
}
A vector is just like a array, except it's size is dynamic and you can resize it at runtime. See the docs for more details.
Here is my code:
fn fetch_transaction (graph: &mut Vec<(&'static str, Transaction)>, tx_id: &'static str) -> Transaction {
for item in graph.iter() {
if item.0 == tx_id {
return item.1;
} else {
println!("Transaction not found.");
}
}
}
By the way, item.1 is a Transaction struct type, while item.0 is a static string. When I compile it, I get this error:
fn fetch_transaction (graph: &mut Vec<(&'static str, Transaction)>, tx_id: &'static str) -> Transaction {
| ----------- expected `Transaction` because of return type
54 | / for item in graph.iter() {
55 | | if item.0 == tx_id {
56 | | return item.1;
57 | | } else {
58 | | println!("Transaction not found.");
59 | | }
60 | | }
| |_____^ expected struct `Transaction`, found `()`
Why is this, and how can I fix it.
Since it is possible that the vector does not contain an item whose item.0 is tx_id, what you probably want to do is return an Option, which will be None if nothing was found.
Something like this:
fn fetch_transaction(graph: &mut Vec<(&'static str, Transaction)>, tx_id: &'static str) -> Option<Transaction> {
graph.iter().find(|i| i.0 == tx_id).and_then(|i| Some(i.1))
}
struct OverflowError {}
fn test_unwrap() -> Result<String, OverflowError> {
let a: Result<String, u8> = Err(100);
let a: String = a.unwrap_or_else(|err| {
if err < 100 {
String::from("Ok")
} else {
// I want to return from the function (not just the closure)
// This is compile error with error:
// "the ? operator can only be used in a closure that returns Result or Option"
Err(OverflowError {})?
}
});
Ok(a)
}
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> src/lib.rs:13:13
|
6 | let a: String = a.unwrap_or_else(|err| {
| ______________________________________-
7 | | if err < 100 {
8 | | String::from("Ok")
9 | | } else {
... |
13 | | Err(OverflowError {})?
| | ^^^^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a closure that returns `std::string::String`
14 | | }
15 | | });
| |_____- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `std::ops::Try` is not implemented for `std::string::String`
= note: required by `std::ops::Try::from_error`
It's the simplified version of my code. Basically inside the unwrap_or_else closure, there might be a conditional error (e.g. IOError). In such cases, I'd like to terminate the function early (using ?). But obviously it doesn't work since it is currently in a closure and the closure doesn't expect a Result type.
What's the best practice to handle this?
What you want is or_else():
struct OverflowError {}
fn test_unwrap() -> Result<String, OverflowError> {
let a: Result<String, u8> = Err(100);
let a: String = a.or_else(|err| {
if err < 100 {
Ok(String::from("Ok"))
} else {
Err(OverflowError {})
}
})?;
Ok(a)
}
Simplified:
struct OverflowError {}
fn test_unwrap() -> Result<String, OverflowError> {
Err(100).or_else(|err| {
if err < 100 {
Ok(String::from("Ok"))
} else {
Err(OverflowError {})
}
})
}
I am trying to wrap a TcpStream and TlsStream in one object so that I can interface with either of them using one struct. I am trying to delegate the io methods to one or the other based on a config value but can't figure out how to return a struct with a generic type that implements the Read and Write traits
My code is as follows
pub struct TcpStream<T: Read + Write> {
io_delegate: T,
config: Config,
}
impl<T> TcpStream<T>
where T: Read + Write
{
pub fn connect<A: ToSocketAddrs>(config: Config, addr: A) -> io::Result<TcpStream<T>> {
let tcp_stream = net::TcpStream::connect(addr).unwrap();
if config.ssl {
let tls_stream = TlsConnector::builder()
.unwrap()
.build()
.unwrap()
.connect("rem", tcp_stream)
.unwrap();
return Ok(TcpStream {
config: config,
io_delegate: tls_stream,
});
}
return Ok(TcpStream {
config: config,
io_delegate: tcp_stream,
});
}
}
When I try to compile I get the following errors
error[E0308]: mismatched types
--> src/rem/tcp_stream.rs:19:23
|
19 | return Ok(TcpStream {
| _______________________^ starting here...
20 | | config: config,
21 | | io_delegate: tls_stream
22 | | });
| |_____________^ ...ending here: expected type parameter, found struct `native_tls::TlsStream`
|
= note: expected type `rem::tcp_stream::TcpStream<T>`
found type `rem::tcp_stream::TcpStream<native_tls::TlsStream<std::net::TcpStream>>`
error[E0308]: mismatched types
--> src/rem/tcp_stream.rs:24:19
|
24 | return Ok(TcpStream{
| ___________________^ starting here...
25 | | config: config,
26 | | io_delegate: tcp_stream
27 | | });
| |_________^ ...ending here: expected type parameter, found struct `std::net::TcpStream`
|
= note: expected type `rem::tcp_stream::TcpStream<T>`
found type `rem::tcp_stream::TcpStream<std::net::TcpStream>`
Is there a way to achieve this sort of thing?
I'm not sure if this is the best solution but it does seem to work.
I created a new trait which is a combination of Read + Write then just stored it as a Box in my struct
trait ReadWrite : Read + Write {}
impl<T: Read + Write> ReadWrite for T {}
pub struct TcpStream{
io_delegate : Box<ReadWrite>,
config: Config
}
impl TcpStream {
pub fn connect<A: ToSocketAddrs>(config: Config, addr: A) -> TcpStream {
let tcp_stream = net::TcpStream::connect(addr).unwrap();
if config.ssl {
let tls_stream = TlsConnector::builder().unwrap().build().unwrap().connect("rem", tcp_stream).unwrap();
return TcpStream {
config: config,
io_delegate: Box::new(tls_stream)
};
}
return TcpStream{
config: config,
io_delegate:Box::new(tcp_stream)
};
}
}