In the example we assume that we have two codes that has their perimeter and tested individually.
The code 1 return the type Option<Rc<RefCell<T>>>
and code 2 that have to consume &T.
I have an issues with the following example playground.
use std::cell::RefCell;
use std::rc::Rc;
// Code 2
fn action(data_1: Option<&i32>, data_2: Option<&i32>) {
println!("data_1:{:?}, data_2:{:?}", data_1, data_2);
}
fn main() {
// Code 1
let data_1 = Some(Rc::new(RefCell::new(2)));
let data_2 = Some(Rc::new(RefCell::new(5)));
let ref_data_1 = data_1.as_ref().map(|r| r.borrow());
let ref_data_2 = data_2.as_ref().map(|r| r.borrow());
action(ref_data_1, ref_data_2); // Error: mismatched types
}
The example fail because of mismatch types between Ref<T> is found and &T is expected.
My only way that I have founded is to change Option<&i32> to Option<Ref<i32>> but that break the interface of code 2.
You can change the action function to accept either Option<&i32> or Option<Ref<i32>> by using a generic argument with a Deref<Target = i32> bound, which both of those types satisfy:
use std::ops::Deref;
fn action<T>(data_1: Option<T>, data_2: Option<T>)
where
T: Deref<Target = i32>,
{
println!("data_1:{:?}, data_2:{:?}", data_1.as_deref(), data_2.as_deref());
}
If you can't or don't want to change the signature of action, then you can call as_deref() on the arguments when you pass them in:
action(ref_data_1.as_deref(), ref_data_2.as_deref());
Related
fn main() {
fn calculate_two_numbers<T:Debug, const N: usize>(data_set: [T;N]) -> (i32,i32) {
// Key Code
let a = &data_set[0]+&data_set[1];
println!("{:?},{:?},{:?}",&data_set[0],&data_set[1],a);
// Ignore
return (0,0)
}
let data = [1509,1857,1736,1815,1576];
let result = calculate_two_numbers(data);
}
I have a very simple function which takes a list of size n.
From this list, I want to take the first two variables and add them together. I then want to print all of them out with println!.
However, I get the error error[E0369]: cannot add &T to &T
This is the solution the complier suggests but I have trouble understanding it
fn calculate_two_numbers<T:Debug + std::ops::Add<Output = &T>, const N: usize>(data_set: [T;N])
Can someone explain what std::ops::Add<Output = &T> does?
In Rust, when using generic types, you need to tell the compiler what kind of operations you would want to do with the type. This is done be constraining the bounds of your type (You are already using it with Debug, it means you can use it as it were a debug type).
The compiler suggest to add the Add trait, but it will not really work straight away because of the references. You can either add a lifetime or use Copy (with would be probably desirable if you are gonna work with numbers), again add it as another bound:
use std::fmt::Debug;
use std::ops::Add;
fn calculate_two_numbers<T: Debug + Add::<Output=T> + Copy, const N: usize>(data_set: [T; N]) -> (i32, i32) {
// Key Code
let a = data_set[0] + data_set[1];
println!("{:?},{:?},{:?}", data_set[0], data_set[1], a);
// Ignore
return (0, 0);
}
fn main() {
let data = [1509, 1857, 1736, 1815, 1576];
let result = calculate_two_numbers(data);
}
Playground
If using the references approach, you need to specify that for any lifetime for your references that reference implements Add<Output=T>:
fn calculate_two_numbers<T: Debug, const N: usize>(data_set: [T; N]) -> (i32, i32)
where
for<'a> &'a T: Add<Output = T>,
{
// Key Code
let a = &data_set[0] + &data_set[1];
println!("{:?},{:?},{:?}", data_set[0], data_set[1], a);
// Ignore
return (0, 0);
}
Playground
So I have this function
fn render_i32(n: &dyn Typeable, echo: &dyn Fn(&String)) {
let x: &i32 = unsafe {transmute(n)};
echo(&x.to_string());
}
It does not compile because cannot transmute between types of different sizes.
What I want with this code is the following: I have a HashMap which contains rendering functions for different types. Every type that may be rendered must implement my interface Typeable, which basically only returns a constant type_id for the type (I've just come across a type_id in std, and wonder if I could use that instead...). And using that type_id I can then look up the correct render function in my HashMap. So my code ensures, that render_i32 is only called for i32. This works fine.
Now all of this would be really easy in C where I'd just cast the value under the pointer. But in rust it does not appear to be so easy. I don't get at the i32 value. How would I get that?
Edit: Alternative Solutions to my own approach that are less type-unsafe but solve the following requirement are also welcome: clients (who use this library) should be able to add their own rendering functions for their own types...
Note that the rendering functions are not supposed to be statically defined once: different rendering functions might be used for the same type depending for example on a language setting.
I still don't get why you didn't use the conventional trait-impl approach, it seems to do what you wanted, except that function pointers don't have any common data structure holding them (it's probably less cache-friendly than HashMap's approach)
Playground
use std::iter;
// lib
fn echo_windows(s: &String) {
println!("C:/Users> {}", s)
}
fn echo_linux(s: &String) {
println!("$ {}", s)
}
trait Renderable {
fn render(&self, echo: &dyn Fn(&String));
}
// client
struct ClientType {
ch: char,
len: usize,
}
impl Renderable for ClientType {
fn render(&self, echo: &dyn Fn(&String)) {
let to_echo: String = iter::repeat(self.ch)
.take(self.len)
.collect();
echo(&to_echo);
}
}
fn main() {
ClientType{ ch: '#', len: 5 }.render(&echo_windows); // output: C:/Users> #####
ClientType{ ch: '!', len: 3 }.render(&echo_linux); // output: $ !!!
}
Maybe you can use the Any trait for your purpose:
use std::any::Any;
pub trait Typeable {
...
fn as_any(&self) -> &dyn Any;
}
fn render_i32(n: &dyn Typeable, echo: &dyn Fn(&String)) {
let x: &i32 = n.as_any().downcast_ref::<i32>().unwrap();
echo(&x.to_string());
}
The downcast_ref::<i32>() method returns an Option<&i32>, so you can also check if the downcast is valid. You can even do this in a generic way:
fn render<T:'static + std::fmt::Display>(n: &dyn Typeable, echo: &dyn Fn(&String)) {
let x: &T = n.as_any().downcast_ref::<T>().unwrap();
echo(&x.to_string());
}
To reduce lines of code I moved my clap App to another file with something like this:
playground
use clap::{App, AppSettings, Arg, ArgMatches}; // 2.33.3
use std::path::Path;
fn main() {
let s3m_dir = Path::new("/tmp").join(".s3m");
let matches = get_matches(s3m_dir.display().to_string());
println!("{:#?}", matches);
}
pub fn get_matches(home_dir: String) -> ArgMatches<'static> {
App::new("s3m")
.version(env!("CARGO_PKG_VERSION"))
.setting(AppSettings::SubcommandsNegateReqs)
.after_help(format!("foo bar: {}", home_dir).as_ref())
.arg(
Arg::with_name("config")
.help("config.yml")
.long("config")
.short("c")
.default_value(&format!("{}/.s3m/config.yml", home_dir))
.required(true)
.value_name("config.yml"),
)
.get_matches()
}
The problem I have is that I don't know how could I use the argument home_dir as the default_value, here:
.default_value(&format!("{}/.s3m/config.yml", home_dir))
The signature for default_value is:
pub fn default_value(self, val: &'a str) -> Self
How could I pass a format!("{}/.s3m/config.yml", home_dir with a lifetime in other to satisfy the signature?
I haven't used clap, so there may be a better approach, but the general Rust solution to this problem is to have some data structure that owns the needed strings, so that the ArgMatches can have a lifetime dependent on it:
struct ArgParser {
home_dir: PathBuf,
default_config: OsString,
}
impl ArgParser {
pub fn new(home_dir: &Path) -> Self {
let default_config_path: PathBuf = home_dir.join(".s3m/config.yml");
Self {
home_dir: home_dir.to_owned(),
default_config: default_config_path.as_os_str().to_owned(),
}
}
I've also adjusted the config path to use Path::join rather than string formatting and OsString instead of String, which aren't actually relevant to your question but should be more correct.
Now we can modify get_matches to work with this, as part of impl ArgParser:
pub fn get_matches(&self) -> ArgMatches {
App::new("s3m")
.version(env!("CARGO_PKG_VERSION"))
.setting(AppSettings::SubcommandsNegateReqs)
.after_help(format!("foo bar: {}", self.home_dir.display()).as_ref())
.arg(
Arg::with_name("config")
.help("config.yml")
.long("config")
.short("c")
.default_value_os(&self.default_config)
.required(true)
.value_name("config.yml"),
)
.get_matches()
}
}
Notice that there is no lifetime parameter given for ArgMatches. This is because the compiler will automatically infer the lifetime for us, as if we had written:
pub fn get_matches<'a>(&'a self) -> ArgMatches<'a> {...}
The lifetime is no longer 'static, but it can't be 'static (unless you choose to leak the strings that you're configuring App with). Instead, if you you need a string to live longer than the ArgParser, use .to_owned() to convert &'a str into a String that can live independently.
playground
I'm trying to understand how trait objects are implemented in Rust. Please let me know if the following understanding is correct.
I have a function that takes any type that implements the Write trait:
fn some_func(write_to: &mut Write) {}
In any place where we have a type that implements this trait and calls the above function, the compiler generates a "trait object", probably by adding a call to TraitObject::new(data, vtable).
If we have something like:
let input = get_user_input(); // say we are expecting the input to be 1 or 2
let mut file = File::new("blah.txt").unwrap();
let mut vec: Vec<u8> = vec![1, 2, 3];
match input {
1 => some_func(&mut file),
2 => some_func(&mut vec),
}
will probably turn out to be:
match input {
1 => {
let file_write_trait_object: &mut Write =
TraitObject::new(&file, &vtable_for_file_write_trait);
some_func(file_write_trait_object);
}
2 => {
let vec_write_trait_object: &mut Write =
TraitObject::new(&vec, &vtable_for_vec_write_trait);
some_func(vec_write_trait_object);
}
}
Inside some_func the compiler will just access the methods used based on the vtable in the TraitObject passed along.
Trait objects are fat pointers, so fn some_func(write_to: &mut Write) compiles to something like fn some_func(_: *mut OpaqueStruct, _: *const WriteVtable).
I'm trying to implement a method that looks like:
fn concretify<T: Any>(rc: Rc<Any>) -> Option<T> {
Rc::try_unwrap(rc).ok().and_then(|trait_object| {
let b: Box<Any> = unimplemented!();
b.downcast().ok().map(|b| *b)
})
}
However, try_unwrap doesn't work on trait objects (which makes sense, as they're unsized). My next thought was to try to find some function that unwraps Rc<Any> into Box<Any> directly. The closest thing I could find would be
if Rc::strong_count(&rc) == 1 {
Some(unsafe {
Box::from_raw(Rc::into_raw(rc))
})
} else {
None
}
However, Rc::into_raw() appears to require that the type contained in the Rc to be Sized, and I'd ideally not like to have to use unsafe blocks.
Is there any way to implement this?
Playground Link, I'm looking for an implementation of rc_to_box here.
Unfortunately, it appears that the API of Rc is lacking the necessary method to be able to get ownership of the wrapped type when it is !Sized.
The only method which may return the interior item of a Rc is Rc::try_unwrap, however it returns Result<T, Rc<T>> which requires that T be Sized.
In order to do what you wish, you would need to have a method with a signature: Rc<T> -> Result<Box<T>, Rc<T>>, which would allow T to be !Sized, and from there you could extract Box<Any> and perform the downcast call.
However, this method is impossible due to how Rc is implemented. Here is a stripped down version of Rc:
struct RcBox<T: ?Sized> {
strong: Cell<usize>,
weak: Cell<usize>,
value: T,
}
pub struct Rc<T: ?Sized> {
ptr: *mut RcBox<T>,
_marker: PhantomData<T>,
}
Therefore, the only Box you can get out of Rc<T> is Box<RcBox<T>>.
Note that the design is severely constrained here:
single-allocation mandates that all 3 elements be in a single struct
T: ?Sized mandates that T be the last field
so there is little room for improvement in general.
However, in your specific case, it is definitely possible to improve on the generic situation. It does, of course, require unsafe code. And while it works fairly well with Rc, implementing it with Arc would be complicated by the potential data-races.
Oh... and the code is provided as is, no warranty implied ;)
use std::any::Any;
use std::{cell, mem, ptr};
use std::rc::Rc;
struct RcBox<T: ?Sized> {
strong: cell::Cell<usize>,
_weak: cell::Cell<usize>,
value: T,
}
fn concretify<T: Any>(rc: Rc<Any>) -> Option<T> {
// Will be responsible for freeing the memory if there is no other weak
// pointer by the end of this function.
let _guard = Rc::downgrade(&rc);
unsafe {
let killer: &RcBox<Any> = {
let killer: *const RcBox<Any> = mem::transmute(rc);
&*killer
};
if killer.strong.get() != 1 { return None; }
// Do not forget to decrement the count if we do take ownership,
// as otherwise memory will not get released.
let result = killer.value.downcast_ref().map(|r| {
killer.strong.set(0);
ptr::read(r as *const T)
});
// Do not forget to destroy the content of the box if we did not
// take ownership
if result.is_none() {
let _: Rc<Any> = mem::transmute(killer as *const RcBox<Any>);
}
result
}
}
fn main() {
let x: Rc<Any> = Rc::new(1);
println!("{:?}", concretify::<i32>(x));
}
I don't think it's possible to implement your concretify function if you're expecting it to move the original value back out of the Rc; see this question for why.
If you're willing to return a clone, it's straightforward:
fn concretify<T: Any+Clone>(rc: Rc<Any>) -> Option<T> {
rc.downcast_ref().map(Clone::clone)
}
Here's a test:
#[derive(Debug,Clone)]
struct Foo(u32);
#[derive(Debug,Clone)]
struct Bar(i32);
fn main() {
let rc_foo: Rc<Any> = Rc::new(Foo(42));
let rc_bar: Rc<Any> = Rc::new(Bar(7));
let foo: Option<Foo> = concretify(rc_foo);
println!("Got back: {:?}", foo);
let bar: Option<Foo> = concretify(rc_bar);
println!("Got back: {:?}", bar);
}
This outputs:
Got back: Some(Foo(42))
Got back: None
Playground
If you want something more "movey", and creating your values is cheap, you could also make a dummy, use downcast_mut() instead of downcast_ref(), and then std::mem::swap with the dummy.