Related
I'm looking for information and good practices for using MaybeUninit
to directly initialize collections (typically arrays or vectors) and
drop them properly if initialization failed.
Thanks to the API examples, I was able to get by fairly quickly with
arrays but it was much trickier with vectors. On the example that
follows (which is a toy simplification of what I did in my project),
generic function, try_new<T: TryFrom<()>, A:ArrayUninit<T>>(len: usize), tries to create an array or a vector of objects T by means
of a fallible data generator TryFrom::try_from(_:()) implemented by
T. The order in which the array is generated is random
(asynchronism); this is simulated by function indices(len:usize).
Function, try_new<A:ArrayUninit>(len: usize), uses method
ArrayUninit::try_uninit(len: usize), implemented by Vec<Data> and
[Data;N], for building uninitialized array or vector.
In our main, we use data type, Data, as example, for which
generator, TryFrom<()> is implemented.
The following code seems to work, but I'm wondering
how to drop uninitialized data:
(playground)
use core::{ time::Duration, mem::MaybeUninit, };
use std::thread;
use rand::prelude::*;
// trait with method for building uninited array/vector
// implementations for Vec<T> and [T;N] after the main()
trait ArrayUninit<T>: AsMut<[T]> + Sized {
fn try_uninit(len: usize) -> Result<MaybeUninit<Self>,String>;
}
// generate shuffled indices
fn indices(len: usize) -> Box<dyn Iterator<Item = usize>> {
let mut vec: Vec<usize> = (0..len).collect();
vec.shuffle(&mut thread_rng());
Box::new(vec.into_iter())
}
// try to build an array or a vector of objects T
fn try_new<T: TryFrom<()>, A:ArrayUninit<T>>(len: usize) -> Result<A,String> {
// build uninitialized collection
let mut uninited = A::try_uninit(len)?;
// simulate initialization in random order
let indices = indices(len);
// build a mutable ref to the array/vector
let ra: &mut A = unsafe {(uninited.as_mut_ptr() as *mut A).as_mut() }.unwrap();
let mut failed = false;
for i in indices {
// get ptr at i
let ptr_arr: * mut T = unsafe{AsMut::<[T]>::as_mut(ra).as_mut_ptr().add(i)};
// get object and break if failed
let data = match T::try_from(()) {
Ok(data) => data, Err(_) => { failed = true; break; },
};
// set object
unsafe { *ptr_arr = data };
}
if !failed {
Ok(unsafe{ uninited.assume_init() }) // return array, if successful
} else {
// if failed, then
for i in 0..len { // drop all objects within array/vector
let ptr_arr: * mut T = unsafe{AsMut::<[T]>::as_mut(ra).as_mut_ptr().add(i)};
drop(unsafe { ptr_arr.read() });
}
drop(uninited); // and drop uninited array/vector
Err(format!("failed to init"))
}
}
// Object Data
#[derive(Debug)]
struct Data(f64);
impl TryFrom<()> for Data {
type Error = ();
// generate a float with errors; time consuming
fn try_from(_:()) -> Result<Self,()> {
thread::sleep(Duration::from_millis(10));
let f = rand::random();
if f <= 0.99 { Ok(Data(f)) } else { Err(()) }
}
}
fn main() {
let result: Result<Vec<Data>,_> = try_new(3);
println!("result: {:?}",result);
let result: Result<[Data;3],_> = try_new(3);
println!("result: {:?}",result);
let result: Result<Vec<Data>,_> = try_new(1000);
println!("result: {:?}",result);
let result: Result<[Data;1000],_> = try_new(1000);
println!("result: {:?}",result);
}
impl<T> ArrayUninit<T> for Vec<T> {
fn try_uninit(len: usize) -> Result<MaybeUninit<Self>,String> {
let mut v: MaybeUninit<Vec<T>> = MaybeUninit::uninit();
let mut vv = Vec::with_capacity(len);
unsafe { vv.set_len(len) };
v.write(vv);
Ok(v)
}
}
impl<T,const N: usize> ArrayUninit<T> for [T;N] {
fn try_uninit(len: usize) -> Result<MaybeUninit<Self>,String> {
if len == N {
Ok(MaybeUninit::uninit())
} else { Err(format!("len differs from array size")) }
}
}
Here is an example of run (results are random):
Standard Error
Compiling playground v0.0.1 (/playground)
Finished dev [unoptimized + debuginfo] target(s) in 0.84s
Running `target/debug/playground`
Standard Output
result: Ok([Data(0.9778296353515407), Data(0.9319034033060891), Data(0.11046580243682291)])
result: Ok([Data(0.749182522350767), Data(0.5432451150541627), Data(0.6840763419767837)])
result: Err("failed to init")
result: Err("failed to init")
For now, in case of failure, I drop all the addresses within the
array/vector, both initialized and uninitialized, then I drop the
array/vector. It seems to work, but I'm surprised that one can also
drop uninitialized data.
Can anyone confirm if this is a right approach to drop the
uninitialized data? If not, what are the rules to follow?
[EDIT:]
Thanks to the remarks of isaactfa and Chayim, I updated the code as follows (playgroud):
use core::{ time::Duration, mem::MaybeUninit, };
use std::thread;
use rand::prelude::*;
// trait with method for building uninited array/vector
// implementations for Vec<T> and [T;N] after the main()
trait ArrayUninit<T>: AsMut<[T]> + Sized {
type Uninited: Sized;
fn try_uninit(len: usize) -> Result<Self::Uninited,String>;
unsafe fn set(uninit: &mut Self::Uninited, i: usize, t: T);
unsafe fn destructor(uninit: &mut Self::Uninited,);
unsafe fn finalize(uninit: Self::Uninited) -> Self;
}
// generate shuffled indices
fn indices(len: usize) -> Box<dyn Iterator<Item = usize>> {
let mut vec: Vec<usize> = (0..len).collect();
vec.shuffle(&mut thread_rng());
Box::new(vec.into_iter())
}
// try to build an array or a vector of objects T
fn try_new<T: TryFrom<()>, A:ArrayUninit<T>>(len: usize) -> Result<A,String> {
// build uninitialized collection
let mut uninited = A::try_uninit(len)?;
// simulate initialization in random order
let indices = indices(len);
let mut failed = false;
for i in indices {
// get object and break if failed
let data = match T::try_from(()) {
Ok(data) => { data }, Err(_) => { failed = true; break; },
};
// set object
unsafe { A::set(&mut uninited,i,data) };
}
if !failed {
Ok(unsafe{ A::finalize(uninited) }) // return array, if successful
} else {
unsafe { A::destructor(&mut uninited) };
Err(format!("failed to init"))
}
}
// Object Data
#[derive(Debug)]
struct Data(String);
impl TryFrom<()> for Data {
type Error = ();
// generate a float with errors; time consuming
fn try_from(_:()) -> Result<Self,()> {
thread::sleep(Duration::from_millis(10));
let f:f32 = rand::random();
if f <= 0.99 { Ok(Data(format!("Value = {}",f))) } else { Err(()) }
}
}
fn main() {
let result: Result<Vec<Data>,_> = try_new(3);
println!("result: {:?}",result);
let result: Result<[Data;3],_> = try_new(3);
println!("result: {:?}",result);
let result: Result<Vec<Data>,_> = try_new(3);
println!("result: {:?}",result);
let result: Result<[Data;3],_> = try_new(3);
println!("result: {:?}",result);
let result: Result<Vec<Data>,_> = try_new(1000);
println!("result: {:?}",result);
let result: Result<[Data;1000],_> = try_new(1000);
println!("result: {:?}",result);
let result: Result<Vec<Data>,_> = try_new(1000);
println!("result: {:?}",result);
let result: Result<[Data;1000],_> = try_new(1000);
println!("result: {:?}",result);
}
impl<T> ArrayUninit<T> for Vec<T> {
type Uninited = (Vec<T>,Vec<bool>);
fn try_uninit(len: usize) -> Result<Self::Uninited,String> {
Ok((Vec::with_capacity(len),vec![false;len]))
}
unsafe fn set((uninit,flag): &mut Self::Uninited, i: usize, t: T) {
uninit.as_mut_ptr().offset(i as isize).write(t); flag[i] = true;
}
unsafe fn destructor((uninit,flag): &mut Self::Uninited,) {
for i in 0..flag.len() {
if flag[i] { std::ptr::drop_in_place(uninit.as_mut_ptr().offset(i as isize)); }
}
}
unsafe fn finalize((mut uninit,flag): Self::Uninited) -> Self {
uninit.set_len(flag.len());
uninit
}
}
impl<T,const N: usize> ArrayUninit<T> for [T;N] {
type Uninited = ([MaybeUninit<T>;N],[bool;N]);
fn try_uninit(len: usize) -> Result<Self::Uninited,String> {
if len == N {
let uninit = unsafe{ MaybeUninit::uninit().assume_init() };
Ok((uninit,[false;N]))
} else { Err(format!("len differs from array size")) }
}
unsafe fn set((uninit,flag): &mut Self::Uninited, i: usize, t: T) {
uninit[i].write(t); flag[i] = true;
}
unsafe fn destructor((uninit,flag): &mut Self::Uninited,) {
for i in 0..N {
if flag[i] { std::ptr::drop_in_place(uninit[i].as_mut_ptr()); }
}
}
unsafe fn finalize((uninit,_): Self::Uninited) -> Self {
(&uninit as *const _ as *const Self).read()
}
}
The idea here is to use specific approaches for arrays and vecs, which are encoded within trait ArrayUninit. MaybeUninit is used only for arrays, while it is not needed for vecs.
Your code contains multiple points of UB:
Calling set_len() when the elements in range are uninitialized (you're doing that in try_uninit() for Vec<T>) is UB (see set_len()'s docs).
When initializing arrays, you create uninitialized storage for the array in try_uninit() and then turns that into a reference to an initialized array in try_new(). This may be undefined behavior (but not necessarily), see https://github.com/rust-lang/unsafe-code-guidelines/issues/84.
When setting the value at the index (unsafe { *ptr_arr = data } in try_new()), you drop the old value. If the value has no drop glue this is likely fine, but if it has this is undefined behavior since your drop uninitialized data. You need to use std::ptr::write() instead.
You're doing a typed copy of the values by drop(unsafe { ptr_arr.read() }). Doing a typed copy of uninitialized values is definitely UB (Miri is even flagging this one).
As the title says, we have an application which runs wasm plugins. Each plugin can register their own State in the StateRegistry. Then whenever the plugins are executed, they will modify their respective State.
The code below illustrates this:
use std::{collections::HashMap, any::Any};
type ContractId = String;
type GenericContractState = Box<dyn Any>;
// This will be WASM code
mod foo_contract {
use super::StateRegistry;
pub struct State {
}
pub fn update(states: &mut StateRegistry) {
let state = states.states.get_mut(&"foo_contract".to_string()).unwrap();
let state = state.downcast_mut::<State>().unwrap();
// I'd prefer something like:
// let state = state.lookup::<State>().unwrap()
}
}
pub struct StateRegistry {
pub states: HashMap<ContractId, GenericContractState>,
}
impl StateRegistry {
fn new() -> Self {
Self { states: HashMap::new() }
}
fn register(&mut self, contract_id: ContractId, state: GenericContractState) {
self.states.insert(contract_id, state);
}
/*
fn lookup<'a, S>(&'a mut self, contract_id: &ContractId) -> Option<StateRefWrapper<'a, S>> {
match self.states.get_mut(contract_id) {
Some(state) => {
let ptr = state.downcast_mut::<S>();
match ptr {
Some(ptr) => Some(StateRefWrapper { _mut: state, ptr }),
None => None,
}
}
None => None,
}
}
*/
}
/*
struct StateRefWrapper<'a, S> {
_mut: &'a mut Box<dyn Any>,
ptr: &'a mut S,
}
*/
fn main() {
let mut states = StateRegistry::new();
let foo_state = Box::new(foo_contract::State {});
states.register("foo_contract".to_string(), foo_state);
foo_contract::update(&mut states);
}
The part I want to improve is that currently the plugin developer has to use this code to lookup their State in the registry:
let state = states.states.get_mut(&"foo_contract".to_string()).unwrap();
let state = state.downcast_mut::<State>().unwrap();
I want to create a respective method in StateRegistry that is usable like this:
let state = state.lookup::<State>().unwrap()
My attempt (commented above) was something like this:
impl StateRegistry {
// ...
fn lookup<'a, S>(&'a mut self, contract_id: &ContractId) -> Option<StateRefWrapper<'a, S>> {
match self.states.get_mut(contract_id) {
Some(state) => {
let ptr = state.downcast_mut::<S>();
match ptr {
Some(ptr) => Some(StateRefWrapper { _mut: state, ptr }),
None => None,
}
}
None => None,
}
}
}
struct StateRefWrapper<'a, S> {
_mut: &'a mut Box<dyn Any>,
ptr: &'a mut S,
}
How can I get this lookup() function working?
Thanks
You can't construct your StateRefWrapper in the way you want to, because it would contain two mutable borrows of the same value. You can, however, return just a &mut S which seems sufficient:
fn lookup<'a, S: 'static>(&'a mut self, contract_id: &ContractId) -> Option<&'a mut S> {
self.states
.get_mut(contract_id)
.and_then(|state| state.downcast_mut())
}
And use it like this:
pub fn update(states: &mut StateRegistry) {
let state = states.lookup::<State>(&"foo_contract".to_string()).unwrap();
// state.modify();
}
I have the following:
let mut my_number = 32.90;
How do I print the type of my_number?
Using type and type_of did not work. Is there another way I can print the number's type?
You can use the std::any::type_name function. This doesn't need a nightly compiler or an external crate, and the results are quite correct:
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
fn main() {
let s = "Hello";
let i = 42;
print_type_of(&s); // &str
print_type_of(&i); // i32
print_type_of(&main); // playground::main
print_type_of(&print_type_of::<i32>); // playground::print_type_of<i32>
print_type_of(&{ || "Hi!" }); // playground::main::{{closure}}
}
Be warned: as said in the documentation, this information must be used for a debug purpose only:
This is intended for diagnostic use. The exact contents and format of the string are not specified, other than being a best-effort description of the type.
If you want your type representation to stay the same between compiler versions, you should use a trait, like in the phicr's answer.
If you merely wish to find out the type of a variable and are willing to do it at compile time, you can cause an error and get the compiler to pick it up.
For example, set the variable to a type which doesn't work:
let mut my_number: () = 32.90;
// let () = x; would work too
error[E0308]: mismatched types
--> src/main.rs:2:29
|
2 | let mut my_number: () = 32.90;
| ^^^^^ expected (), found floating-point number
|
= note: expected type `()`
found type `{float}`
Or call an invalid method:
let mut my_number = 32.90;
my_number.what_is_this();
error[E0599]: no method named `what_is_this` found for type `{float}` in the current scope
--> src/main.rs:3:15
|
3 | my_number.what_is_this();
| ^^^^^^^^^^^^
Or access an invalid field:
let mut my_number = 32.90;
my_number.what_is_this
error[E0610]: `{float}` is a primitive type and therefore doesn't have fields
--> src/main.rs:3:15
|
3 | my_number.what_is_this
| ^^^^^^^^^^^^
These reveal the type, which in this case is actually not fully resolved. It’s called “floating-point variable” in the first example, and “{float}” in all three examples; this is a partially resolved type which could end up f32 or f64, depending on how you use it. “{float}” is not a legal type name, it’s a placeholder meaning “I’m not completely sure what this is”, but it is a floating-point number. In the case of floating-point variables, if you don't constrain it, it will default to f64¹. (An unqualified integer literal will default to i32.)
See also:
What is the {integer} or {float} in a compiler error message?
¹ There may still be ways of baffling the compiler so that it can’t decide between f32 and f64; I’m not sure. It used to be as simple as 32.90.eq(&32.90), but that treats both as f64 now and chugs along happily, so I don’t know.
There is an unstable function std::intrinsics::type_name that can get you the name of a type, though you have to use a nightly build of Rust (this is unlikely to ever work in stable Rust). Here’s an example:
#![feature(core_intrinsics)]
fn print_type_of<T>(_: &T) {
println!("{}", unsafe { std::intrinsics::type_name::<T>() });
}
fn main() {
print_type_of(&32.90); // prints "f64"
print_type_of(&vec![1, 2, 4]); // prints "std::vec::Vec<i32>"
print_type_of(&"foo"); // prints "&str"
}
If you know all the types beforehand, you can use traits to add a type_of method:
trait TypeInfo {
fn type_of(&self) -> &'static str;
}
impl TypeInfo for i32 {
fn type_of(&self) -> &'static str {
"i32"
}
}
impl TypeInfo for i64 {
fn type_of(&self) -> &'static str {
"i64"
}
}
//...
No intrisics or nothin', so although more limited this is the only solution here that gets you a string and is stable. (see Boiethios's answer) However, it's very laborious and doesn't account for type parameters, so we could...
trait TypeInfo {
fn type_name() -> String;
fn type_of(&self) -> String;
}
macro_rules! impl_type_info {
($($name:ident$(<$($T:ident),+>)*),*) => {
$(impl_type_info_single!($name$(<$($T),*>)*);)*
};
}
macro_rules! mut_if {
($name:ident = $value:expr, $($any:expr)+) => (let mut $name = $value;);
($name:ident = $value:expr,) => (let $name = $value;);
}
macro_rules! impl_type_info_single {
($name:ident$(<$($T:ident),+>)*) => {
impl$(<$($T: TypeInfo),*>)* TypeInfo for $name$(<$($T),*>)* {
fn type_name() -> String {
mut_if!(res = String::from(stringify!($name)), $($($T)*)*);
$(
res.push('<');
$(
res.push_str(&$T::type_name());
res.push(',');
)*
res.pop();
res.push('>');
)*
res
}
fn type_of(&self) -> String {
$name$(::<$($T),*>)*::type_name()
}
}
}
}
impl<'a, T: TypeInfo + ?Sized> TypeInfo for &'a T {
fn type_name() -> String {
let mut res = String::from("&");
res.push_str(&T::type_name());
res
}
fn type_of(&self) -> String {
<&T>::type_name()
}
}
impl<'a, T: TypeInfo + ?Sized> TypeInfo for &'a mut T {
fn type_name() -> String {
let mut res = String::from("&mut ");
res.push_str(&T::type_name());
res
}
fn type_of(&self) -> String {
<&mut T>::type_name()
}
}
macro_rules! type_of {
($x:expr) => { (&$x).type_of() };
}
Let's use it:
impl_type_info!(i32, i64, f32, f64, str, String, Vec<T>, Result<T,S>)
fn main() {
println!("{}", type_of!(1));
println!("{}", type_of!(&1));
println!("{}", type_of!(&&1));
println!("{}", type_of!(&mut 1));
println!("{}", type_of!(&&mut 1));
println!("{}", type_of!(&mut &1));
println!("{}", type_of!(1.0));
println!("{}", type_of!("abc"));
println!("{}", type_of!(&"abc"));
println!("{}", type_of!(String::from("abc")));
println!("{}", type_of!(vec![1,2,3]));
println!("{}", <Result<String,i64>>::type_name());
println!("{}", <&i32>::type_name());
println!("{}", <&str>::type_name());
}
output:
i32
&i32
&&i32
&mut i32
&&mut i32
&mut &i32
f64
&str
&&str
String
Vec<i32>
Result<String,i64>
&i32
&str
Rust Playground
UPD The following does not work anymore. Check Shubham's answer for correction.
Check out std::intrinsics::get_tydesc<T>(). It is in "experimental" state right now, but it's OK if you are just hacking around the type system.
Check out the following example:
fn print_type_of<T>(_: &T) -> () {
let type_name =
unsafe {
(*std::intrinsics::get_tydesc::<T>()).name
};
println!("{}", type_name);
}
fn main() -> () {
let mut my_number = 32.90;
print_type_of(&my_number); // prints "f64"
print_type_of(&(vec!(1, 2, 4))); // prints "collections::vec::Vec<int>"
}
This is what is used internally to implement the famous {:?} formatter.
Update, original answer below
How about trait function type_name, which is useful to get type name quickly.
pub trait AnyExt {
fn type_name(&self) -> &'static str;
}
impl<T> AnyExt for T {
fn type_name(&self) -> &'static str {
std::any::type_name::<T>()
}
}
fn main(){
let my_number = 32.90;
println!("{}",my_number.type_name());
}
Output:
f64
Original answer
I write a macro type_of!() to debug, which is original from std dbg!().
pub fn type_of2<T>(v: T) -> (&'static str, T) {
(std::any::type_name::<T>(), v)
}
#[macro_export]
macro_rules! type_of {
// NOTE: We cannot use `concat!` to make a static string as a format argument
// of `eprintln!` because `file!` could contain a `{` or
// `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
// will be malformed.
() => {
eprintln!("[{}:{}]", file!(), line!());
};
($val:expr $(,)?) => {
// Use of `match` here is intentional because it affects the lifetimes
// of temporaries - https://stackoverflow.com/a/48732525/1063961
match $val {
tmp => {
let (type_,tmp) = $crate::type_of2(tmp);
eprintln!("[{}:{}] {}: {}",
file!(), line!(), stringify!($val), type_);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($($crate::type_of!($val)),+,)
};
}
fn main(){
let my_number = type_of!(32.90);
type_of!(my_number);
}
Output:
[src/main.rs:32] 32.90: f64
[src/main.rs:33] my_number: f64
** UPDATE ** This has not been verified to work any time recently.
I put together a little crate to do this based off vbo's answer. It gives you a macro to return or print out the type.
Put this in your Cargo.toml file:
[dependencies]
t_bang = "0.1.2"
Then you can use it like so:
#[macro_use] extern crate t_bang;
use t_bang::*;
fn main() {
let x = 5;
let x_type = t!(x);
println!("{:?}", x_type); // prints out: "i32"
pt!(x); // prints out: "i32"
pt!(5); // prints out: "i32"
}
You can also use the simple approach of using the variable in println!("{:?}", var). If Debug is not implemented for the type, you can see the type in the compiler's error message:
mod some {
pub struct SomeType;
}
fn main() {
let unknown_var = some::SomeType;
println!("{:?}", unknown_var);
}
(playpen)
It's dirty but it works.
There's a #ChrisMorgan answer to get approximate type ("float") in stable rust and there's a #ShubhamJain answer to get precise type ("f64") through unstable function in nightly rust.
Now here's a way one can get precise type (ie decide between f32 and f64) in stable rust:
fn main() {
let a = 5.;
let _: () = unsafe { std::mem::transmute(a) };
}
results in
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> main.rs:3:27
|
3 | let _: () = unsafe { std::mem::transmute(a) };
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `f64` (64 bits)
= note: target type: `()` (0 bits)
Update
The turbofish variation
fn main() {
let a = 5.;
unsafe { std::mem::transmute::<_, ()>(a) }
}
is slightly shorter but somewhat less readable.
Some other answers don't work, but I find that the typename crate works.
Create a new project:
cargo new test_typename
Modify the Cargo.toml
[dependencies]
typename = "0.1.1"
Modify your source code
use typename::TypeName;
fn main() {
assert_eq!(String::type_name(), "std::string::String");
assert_eq!(Vec::<i32>::type_name(), "std::vec::Vec<i32>");
assert_eq!([0, 1, 2].type_name_of(), "[i32; 3]");
let a = 65u8;
let b = b'A';
let c = 65;
let d = 65i8;
let e = 65i32;
let f = 65u32;
let arr = [1,2,3,4,5];
let first = arr[0];
println!("type of a 65u8 {} is {}", a, a.type_name_of());
println!("type of b b'A' {} is {}", b, b.type_name_of());
println!("type of c 65 {} is {}", c, c.type_name_of());
println!("type of d 65i8 {} is {}", d, d.type_name_of());
println!("type of e 65i32 {} is {}", e, e.type_name_of());
println!("type of f 65u32 {} is {}", f, f.type_name_of());
println!("type of arr {:?} is {}", arr, arr.type_name_of());
println!("type of first {} is {}", first, first.type_name_of());
}
The output is:
type of a 65u8 65 is u8
type of b b'A' 65 is u8
type of c 65 65 is i32
type of d 65i8 65 is i8
type of e 65i32 65 is i32
type of f 65u32 65 is u32
type of arr [1, 2, 3, 4, 5] is [i32; 5]
type of first 1 is i32
If your just wanting to know the type of your variable during interactive development, I would highly recommend using rls (rust language server) inside of your editor or ide. You can then simply permanently enable or toggle the hover ability and just put your cursor over the variable. A little dialog should come up with information about the variable including the type.
This is simplified version of #Boiethios answer. I have removed some '&' symbols from original solution.
fn print_type_of<T>(_: T) {
println!("{}", std::any::type_name::<T>())
}
fn main() {
let s = "Hello";
let i = 42;
print_type_of(s); // &str
print_type_of(i); // i32
print_type_of(main); // playground::main
print_type_of(print_type_of::<i32>); // playground::print_type_of<i32>
print_type_of(|| "Hi!" ); // playground::main::{{closure}}
}
View in Rust Playground
Newly added in version 1.38 std::any::type_name
use std::any::type_name;
fn type_of<T>(_: T) -> &'static str {
type_name::<T>()
}
fn main() {
let x = 21;
let y = 2.5;
println!("{}", type_of(&y));
println!("{}", type_of(x));
}
short story;
fn tyof<T>(_: &T) -> String {
std::any::type_name::<T>().into()
}
long story;
trait Type {
fn type_of(&self) -> String;
}
macro_rules! Type {
($($ty:ty),*) => {
$(
impl Type for $ty {
fn type_of(&self) -> String {
stringify!($ty).into()
}
}
)*
}
}
#[rustfmt::skip]
Type!(
u8, i8, u16, i16, u32, i32, i64, u64, i128, String, [()], (), Vec<()>, &u8, &i8, &u16, &i16, &u32, &i32, &i64, &u64, &i128, &str, &[()], &Vec<()>, &()
// add any struct, enum or type you want
);
macro_rules! tyof {
($var: expr) => {{
$var.type_of()
}};
}
fn main() {
let x = "Hello world!";
println!("{}", tyof!(x));
// or
println!("{}", x.type_of());
let x = 5;
println!("{}", tyof!(x));
// or
println!("{}", x.type_of());
}
Macro form permits an usage "everywhere" while the function need an object to be parse.
Macro form (one liner):
macro_rules! ty {($type:ty) => {std::any::type_name::<$type>()}}
Macro form formated:
macro_rules! ty {
($type:ty) => {
std::any::type_name::<$type>()
};
}
Function form (borrowing is to not destroy the parsed var):
fn type_of<T>(_: &T) -> &'static str {std::any::type_name::<T>()}
fn type_of<T>(_: &T) -> &'static str {
std::any::type_name::<T>()
}
Example:
macro_rules! ty {($type:ty) => {std::any::type_name::<$type>()}}
fn type_of<T>(_: &T) -> &'static str {std::any::type_name::<T>()}
struct DontMater<T>(T);
impl<T: std::fmt::Debug> std::fmt::Debug for DontMater<T> {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.write_fmt(format_args!("DontMater<{}>({:?})", ty!(T), self.0))
}
}
fn main() {
type µ = [Vec<String>; 7];
println!("{:?}", DontMater(5_usize));
println!("{:?}", DontMater("¤"));
println!("{}", ty!(char));
println!("{:?}", ty!(µ));
println!("{}", type_of(&DontMater(72_i8)));
println!("{:?}", type_of(&15_f64));
}
Returns:
DontMater<usize>(5)
DontMater<&str>("¤")
char
"[alloc::vec::Vec<alloc::string::String>; 7]"
env_vars::DontMater<i8>
"f64"
I like previous answer by #Coautose very much, but in case anyone wants just the type name without the namespace, for example C instead of a::b::C, here is a modified version of the macro that appears to work as intended:
macro_rules! ty {
($type:ty) => {{
let result = std::any::type_name::<$type>();
match result.rsplit_once(':') {
Some((_, s)) => s,
None => result,
}
}};
}
Usage:
debug!("Testing type name: {}", ty!(A));
Better to use this:
fn print_type_of<T>(_: &T) -> String {
format!("{}", std::any::type_name::<T>())
}
fn main() {
let s = &"hello world".to_string();
let cloned_s = s.clone();
println!("{:?}", print_type_of(&s));
println!("{:?}", print_type_of(&cloned_s));
}
Taken inference from https://stackoverflow.com/a/29168659/6774636
I have the following:
let mut my_number = 32.90;
How do I print the type of my_number?
Using type and type_of did not work. Is there another way I can print the number's type?
You can use the std::any::type_name function. This doesn't need a nightly compiler or an external crate, and the results are quite correct:
fn print_type_of<T>(_: &T) {
println!("{}", std::any::type_name::<T>())
}
fn main() {
let s = "Hello";
let i = 42;
print_type_of(&s); // &str
print_type_of(&i); // i32
print_type_of(&main); // playground::main
print_type_of(&print_type_of::<i32>); // playground::print_type_of<i32>
print_type_of(&{ || "Hi!" }); // playground::main::{{closure}}
}
Be warned: as said in the documentation, this information must be used for a debug purpose only:
This is intended for diagnostic use. The exact contents and format of the string are not specified, other than being a best-effort description of the type.
If you want your type representation to stay the same between compiler versions, you should use a trait, like in the phicr's answer.
If you merely wish to find out the type of a variable and are willing to do it at compile time, you can cause an error and get the compiler to pick it up.
For example, set the variable to a type which doesn't work:
let mut my_number: () = 32.90;
// let () = x; would work too
error[E0308]: mismatched types
--> src/main.rs:2:29
|
2 | let mut my_number: () = 32.90;
| ^^^^^ expected (), found floating-point number
|
= note: expected type `()`
found type `{float}`
Or call an invalid method:
let mut my_number = 32.90;
my_number.what_is_this();
error[E0599]: no method named `what_is_this` found for type `{float}` in the current scope
--> src/main.rs:3:15
|
3 | my_number.what_is_this();
| ^^^^^^^^^^^^
Or access an invalid field:
let mut my_number = 32.90;
my_number.what_is_this
error[E0610]: `{float}` is a primitive type and therefore doesn't have fields
--> src/main.rs:3:15
|
3 | my_number.what_is_this
| ^^^^^^^^^^^^
These reveal the type, which in this case is actually not fully resolved. It’s called “floating-point variable” in the first example, and “{float}” in all three examples; this is a partially resolved type which could end up f32 or f64, depending on how you use it. “{float}” is not a legal type name, it’s a placeholder meaning “I’m not completely sure what this is”, but it is a floating-point number. In the case of floating-point variables, if you don't constrain it, it will default to f64¹. (An unqualified integer literal will default to i32.)
See also:
What is the {integer} or {float} in a compiler error message?
¹ There may still be ways of baffling the compiler so that it can’t decide between f32 and f64; I’m not sure. It used to be as simple as 32.90.eq(&32.90), but that treats both as f64 now and chugs along happily, so I don’t know.
There is an unstable function std::intrinsics::type_name that can get you the name of a type, though you have to use a nightly build of Rust (this is unlikely to ever work in stable Rust). Here’s an example:
#![feature(core_intrinsics)]
fn print_type_of<T>(_: &T) {
println!("{}", unsafe { std::intrinsics::type_name::<T>() });
}
fn main() {
print_type_of(&32.90); // prints "f64"
print_type_of(&vec![1, 2, 4]); // prints "std::vec::Vec<i32>"
print_type_of(&"foo"); // prints "&str"
}
If you know all the types beforehand, you can use traits to add a type_of method:
trait TypeInfo {
fn type_of(&self) -> &'static str;
}
impl TypeInfo for i32 {
fn type_of(&self) -> &'static str {
"i32"
}
}
impl TypeInfo for i64 {
fn type_of(&self) -> &'static str {
"i64"
}
}
//...
No intrisics or nothin', so although more limited this is the only solution here that gets you a string and is stable. (see Boiethios's answer) However, it's very laborious and doesn't account for type parameters, so we could...
trait TypeInfo {
fn type_name() -> String;
fn type_of(&self) -> String;
}
macro_rules! impl_type_info {
($($name:ident$(<$($T:ident),+>)*),*) => {
$(impl_type_info_single!($name$(<$($T),*>)*);)*
};
}
macro_rules! mut_if {
($name:ident = $value:expr, $($any:expr)+) => (let mut $name = $value;);
($name:ident = $value:expr,) => (let $name = $value;);
}
macro_rules! impl_type_info_single {
($name:ident$(<$($T:ident),+>)*) => {
impl$(<$($T: TypeInfo),*>)* TypeInfo for $name$(<$($T),*>)* {
fn type_name() -> String {
mut_if!(res = String::from(stringify!($name)), $($($T)*)*);
$(
res.push('<');
$(
res.push_str(&$T::type_name());
res.push(',');
)*
res.pop();
res.push('>');
)*
res
}
fn type_of(&self) -> String {
$name$(::<$($T),*>)*::type_name()
}
}
}
}
impl<'a, T: TypeInfo + ?Sized> TypeInfo for &'a T {
fn type_name() -> String {
let mut res = String::from("&");
res.push_str(&T::type_name());
res
}
fn type_of(&self) -> String {
<&T>::type_name()
}
}
impl<'a, T: TypeInfo + ?Sized> TypeInfo for &'a mut T {
fn type_name() -> String {
let mut res = String::from("&mut ");
res.push_str(&T::type_name());
res
}
fn type_of(&self) -> String {
<&mut T>::type_name()
}
}
macro_rules! type_of {
($x:expr) => { (&$x).type_of() };
}
Let's use it:
impl_type_info!(i32, i64, f32, f64, str, String, Vec<T>, Result<T,S>)
fn main() {
println!("{}", type_of!(1));
println!("{}", type_of!(&1));
println!("{}", type_of!(&&1));
println!("{}", type_of!(&mut 1));
println!("{}", type_of!(&&mut 1));
println!("{}", type_of!(&mut &1));
println!("{}", type_of!(1.0));
println!("{}", type_of!("abc"));
println!("{}", type_of!(&"abc"));
println!("{}", type_of!(String::from("abc")));
println!("{}", type_of!(vec![1,2,3]));
println!("{}", <Result<String,i64>>::type_name());
println!("{}", <&i32>::type_name());
println!("{}", <&str>::type_name());
}
output:
i32
&i32
&&i32
&mut i32
&&mut i32
&mut &i32
f64
&str
&&str
String
Vec<i32>
Result<String,i64>
&i32
&str
Rust Playground
UPD The following does not work anymore. Check Shubham's answer for correction.
Check out std::intrinsics::get_tydesc<T>(). It is in "experimental" state right now, but it's OK if you are just hacking around the type system.
Check out the following example:
fn print_type_of<T>(_: &T) -> () {
let type_name =
unsafe {
(*std::intrinsics::get_tydesc::<T>()).name
};
println!("{}", type_name);
}
fn main() -> () {
let mut my_number = 32.90;
print_type_of(&my_number); // prints "f64"
print_type_of(&(vec!(1, 2, 4))); // prints "collections::vec::Vec<int>"
}
This is what is used internally to implement the famous {:?} formatter.
Update, original answer below
How about trait function type_name, which is useful to get type name quickly.
pub trait AnyExt {
fn type_name(&self) -> &'static str;
}
impl<T> AnyExt for T {
fn type_name(&self) -> &'static str {
std::any::type_name::<T>()
}
}
fn main(){
let my_number = 32.90;
println!("{}",my_number.type_name());
}
Output:
f64
Original answer
I write a macro type_of!() to debug, which is original from std dbg!().
pub fn type_of2<T>(v: T) -> (&'static str, T) {
(std::any::type_name::<T>(), v)
}
#[macro_export]
macro_rules! type_of {
// NOTE: We cannot use `concat!` to make a static string as a format argument
// of `eprintln!` because `file!` could contain a `{` or
// `$val` expression could be a block (`{ .. }`), in which case the `eprintln!`
// will be malformed.
() => {
eprintln!("[{}:{}]", file!(), line!());
};
($val:expr $(,)?) => {
// Use of `match` here is intentional because it affects the lifetimes
// of temporaries - https://stackoverflow.com/a/48732525/1063961
match $val {
tmp => {
let (type_,tmp) = $crate::type_of2(tmp);
eprintln!("[{}:{}] {}: {}",
file!(), line!(), stringify!($val), type_);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($($crate::type_of!($val)),+,)
};
}
fn main(){
let my_number = type_of!(32.90);
type_of!(my_number);
}
Output:
[src/main.rs:32] 32.90: f64
[src/main.rs:33] my_number: f64
** UPDATE ** This has not been verified to work any time recently.
I put together a little crate to do this based off vbo's answer. It gives you a macro to return or print out the type.
Put this in your Cargo.toml file:
[dependencies]
t_bang = "0.1.2"
Then you can use it like so:
#[macro_use] extern crate t_bang;
use t_bang::*;
fn main() {
let x = 5;
let x_type = t!(x);
println!("{:?}", x_type); // prints out: "i32"
pt!(x); // prints out: "i32"
pt!(5); // prints out: "i32"
}
You can also use the simple approach of using the variable in println!("{:?}", var). If Debug is not implemented for the type, you can see the type in the compiler's error message:
mod some {
pub struct SomeType;
}
fn main() {
let unknown_var = some::SomeType;
println!("{:?}", unknown_var);
}
(playpen)
It's dirty but it works.
There's a #ChrisMorgan answer to get approximate type ("float") in stable rust and there's a #ShubhamJain answer to get precise type ("f64") through unstable function in nightly rust.
Now here's a way one can get precise type (ie decide between f32 and f64) in stable rust:
fn main() {
let a = 5.;
let _: () = unsafe { std::mem::transmute(a) };
}
results in
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> main.rs:3:27
|
3 | let _: () = unsafe { std::mem::transmute(a) };
| ^^^^^^^^^^^^^^^^^^^
|
= note: source type: `f64` (64 bits)
= note: target type: `()` (0 bits)
Update
The turbofish variation
fn main() {
let a = 5.;
unsafe { std::mem::transmute::<_, ()>(a) }
}
is slightly shorter but somewhat less readable.
Some other answers don't work, but I find that the typename crate works.
Create a new project:
cargo new test_typename
Modify the Cargo.toml
[dependencies]
typename = "0.1.1"
Modify your source code
use typename::TypeName;
fn main() {
assert_eq!(String::type_name(), "std::string::String");
assert_eq!(Vec::<i32>::type_name(), "std::vec::Vec<i32>");
assert_eq!([0, 1, 2].type_name_of(), "[i32; 3]");
let a = 65u8;
let b = b'A';
let c = 65;
let d = 65i8;
let e = 65i32;
let f = 65u32;
let arr = [1,2,3,4,5];
let first = arr[0];
println!("type of a 65u8 {} is {}", a, a.type_name_of());
println!("type of b b'A' {} is {}", b, b.type_name_of());
println!("type of c 65 {} is {}", c, c.type_name_of());
println!("type of d 65i8 {} is {}", d, d.type_name_of());
println!("type of e 65i32 {} is {}", e, e.type_name_of());
println!("type of f 65u32 {} is {}", f, f.type_name_of());
println!("type of arr {:?} is {}", arr, arr.type_name_of());
println!("type of first {} is {}", first, first.type_name_of());
}
The output is:
type of a 65u8 65 is u8
type of b b'A' 65 is u8
type of c 65 65 is i32
type of d 65i8 65 is i8
type of e 65i32 65 is i32
type of f 65u32 65 is u32
type of arr [1, 2, 3, 4, 5] is [i32; 5]
type of first 1 is i32
If your just wanting to know the type of your variable during interactive development, I would highly recommend using rls (rust language server) inside of your editor or ide. You can then simply permanently enable or toggle the hover ability and just put your cursor over the variable. A little dialog should come up with information about the variable including the type.
This is simplified version of #Boiethios answer. I have removed some '&' symbols from original solution.
fn print_type_of<T>(_: T) {
println!("{}", std::any::type_name::<T>())
}
fn main() {
let s = "Hello";
let i = 42;
print_type_of(s); // &str
print_type_of(i); // i32
print_type_of(main); // playground::main
print_type_of(print_type_of::<i32>); // playground::print_type_of<i32>
print_type_of(|| "Hi!" ); // playground::main::{{closure}}
}
View in Rust Playground
Newly added in version 1.38 std::any::type_name
use std::any::type_name;
fn type_of<T>(_: T) -> &'static str {
type_name::<T>()
}
fn main() {
let x = 21;
let y = 2.5;
println!("{}", type_of(&y));
println!("{}", type_of(x));
}
short story;
fn tyof<T>(_: &T) -> String {
std::any::type_name::<T>().into()
}
long story;
trait Type {
fn type_of(&self) -> String;
}
macro_rules! Type {
($($ty:ty),*) => {
$(
impl Type for $ty {
fn type_of(&self) -> String {
stringify!($ty).into()
}
}
)*
}
}
#[rustfmt::skip]
Type!(
u8, i8, u16, i16, u32, i32, i64, u64, i128, String, [()], (), Vec<()>, &u8, &i8, &u16, &i16, &u32, &i32, &i64, &u64, &i128, &str, &[()], &Vec<()>, &()
// add any struct, enum or type you want
);
macro_rules! tyof {
($var: expr) => {{
$var.type_of()
}};
}
fn main() {
let x = "Hello world!";
println!("{}", tyof!(x));
// or
println!("{}", x.type_of());
let x = 5;
println!("{}", tyof!(x));
// or
println!("{}", x.type_of());
}
Macro form permits an usage "everywhere" while the function need an object to be parse.
Macro form (one liner):
macro_rules! ty {($type:ty) => {std::any::type_name::<$type>()}}
Macro form formated:
macro_rules! ty {
($type:ty) => {
std::any::type_name::<$type>()
};
}
Function form (borrowing is to not destroy the parsed var):
fn type_of<T>(_: &T) -> &'static str {std::any::type_name::<T>()}
fn type_of<T>(_: &T) -> &'static str {
std::any::type_name::<T>()
}
Example:
macro_rules! ty {($type:ty) => {std::any::type_name::<$type>()}}
fn type_of<T>(_: &T) -> &'static str {std::any::type_name::<T>()}
struct DontMater<T>(T);
impl<T: std::fmt::Debug> std::fmt::Debug for DontMater<T> {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
fmt.write_fmt(format_args!("DontMater<{}>({:?})", ty!(T), self.0))
}
}
fn main() {
type µ = [Vec<String>; 7];
println!("{:?}", DontMater(5_usize));
println!("{:?}", DontMater("¤"));
println!("{}", ty!(char));
println!("{:?}", ty!(µ));
println!("{}", type_of(&DontMater(72_i8)));
println!("{:?}", type_of(&15_f64));
}
Returns:
DontMater<usize>(5)
DontMater<&str>("¤")
char
"[alloc::vec::Vec<alloc::string::String>; 7]"
env_vars::DontMater<i8>
"f64"
I like previous answer by #Coautose very much, but in case anyone wants just the type name without the namespace, for example C instead of a::b::C, here is a modified version of the macro that appears to work as intended:
macro_rules! ty {
($type:ty) => {{
let result = std::any::type_name::<$type>();
match result.rsplit_once(':') {
Some((_, s)) => s,
None => result,
}
}};
}
Usage:
debug!("Testing type name: {}", ty!(A));
Better to use this:
fn print_type_of<T>(_: &T) -> String {
format!("{}", std::any::type_name::<T>())
}
fn main() {
let s = &"hello world".to_string();
let cloned_s = s.clone();
println!("{:?}", print_type_of(&s));
println!("{:?}", print_type_of(&cloned_s));
}
Taken inference from https://stackoverflow.com/a/29168659/6774636
I don't know what to do next. It looks like I misunderstand something, or maybe I have not learned some critical topic.
use std::sync::Arc;
use reqwest::{Error, Response}; // 0.11.4
use tokio::sync::mpsc::{self, Receiver, Sender}; // 1.9.0
pub struct Task {
pub id: u32,
pub url: String,
}
pub enum Message {
Failure(Task, Error),
Success(Task, Response),
}
struct State {
client: reqwest::Client,
res_tx: Sender<Message>,
res_rx: Receiver<Message>,
}
pub struct Proxy {
state: Arc<State>,
max_rps: u16,
max_pending: u16,
id: u32,
parent_tx: Sender<String>,
}
async fn send_msg<T>(tx: &Sender<T>, msg: T) {
match tx.send(msg).await {
Err(error) => {
eprintln!("{}", error)
}
_ => (),
};
}
impl Proxy {
// Starts loop for input channel
async fn start_chin(&mut self) -> Sender<Task> {
let (chin_tx, mut chin_rx) = mpsc::channel::<Task>(self.max_pending as usize + 1 as usize);
let state_outer = self.state.clone();
tokio::spawn(async move {
loop {
match chin_rx.recv().await {
Some(task) => {
let res_tx = state_outer.res_tx.clone();
let state = state_outer.clone();
tokio::spawn(async move {
match state.client.get(&task.url).send().await {
Ok(res) => send_msg(&res_tx, Message::Success(task, res)).await,
Err(err) => send_msg(&res_tx, Message::Failure(task, err)).await,
}
});
}
None => (),
}
}
});
chin_tx
}
async fn start_chres(&self) {
let state = self.state.clone();
tokio::spawn(async move {
loop {
match state.res_rx.recv().await { // LINE PRODUCES ERROR
Some(task) => {}
None => (),
}
}
});
}
}
impl Proxy {
pub fn new(
id: u32,
parent_tx: Sender<String>,
proxy_addr: &str,
max_rps: u16,
max_pending: u16,
) -> Result<Self, Error> {
let client = reqwest::Client::builder();
if proxy_addr != "none" {
client = client.proxy(reqwest::Proxy::all(proxy_addr)?)
}
let (res_tx, res_rx) = mpsc::channel::<Message>(max_pending as usize + 1 as usize); // TODO: check size
Ok(Proxy {
id,
state: Arc::new(State {
client: client.build()?,
res_tx,
res_rx,
}),
max_rps,
max_pending,
parent_tx,
})
}
}
error[E0596]: cannot borrow data in an `Arc` as mutable
--> src/lib.rs:69:23
|
69 | match state.res_rx.recv().await {
| ^^^^^^^^^^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<State>`
use std::sync::Arc;
struct Something {
size: usize
}
impl Something {
fn increase(&mut self) {
self.size = self.size + 1;
}
}
fn main() {
let something = Something{size: 1};
let arc = Arc::new(something);
arc.increase();
}
gives
error[E0596]: cannot borrow data in an `Arc` as mutable
--> src/main.rs:16:5
|
16 | arc.increase();
| ^^^ cannot borrow as mutable
|
= help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<Something>`
error: aborting due to previous error; 1 warning emitted
because it tries to borrow arc as mutable. For it to happen, DerefMut would have to be implemented for Arc but it's not because Arc is not meant to be mutable.
Wraping your object in a Mutex works:
use std::sync::{Arc, Mutex};
struct Something {
size: usize
}
impl Something {
fn increase(&mut self) {
self.size = self.size + 1;
}
}
fn main() {
let something = Something{size: 1};
let arc = Arc::new(Mutex::new(something));
arc.lock().unwrap().increase();
}
Now it can be shared and can be increased.
Lucas Zanella's answer and Shepmaster's comments helped alot to refactor and simplify code. I've desided to pass ownership inside Proxy::new() function instead of using shared reference. The code became more readable, and I've avoided shared reference for mutable tokio::sync::mpsc::Receiver. Perhaps the question turned out to be too unstructured, but I came to a new approach thanks to the community. Refactored code is listed below.
use reqwest::{Client, Error, Response};
use tokio::sync::mpsc;
use tokio::sync::mpsc::{Sender, Receiver};
pub struct Task {
pub id: u32,
pub url: String,
}
pub enum Message{
Failure(Task, Error),
Success(Task, Response),
}
pub struct Proxy{
id: u32,
max_rps: u16,
max_pending: u16,
in_tx: Sender<Task>,
}
async fn send_msg<T>(tx: &Sender<T>, msg: T){
match tx.send(msg).await {
Err(error) => { eprintln!("{}", error) },
_ => (),
};
}
async fn start_loop_in(client: Client, mut in_rx: Receiver<Task>, res_tx: Sender<Message>){
loop {
if let Some(task) = in_rx.recv().await {
let client_clone = client.clone();
let res_tx_clone = res_tx.clone();
tokio::spawn(async move {
println!("SENDING: {}", &task.url); // TODO: DELETE DEBUG
match client_clone.get(&task.url).send().await {
Ok(res) => send_msg(&res_tx_clone, Message::Success(task, res)).await,
Err(err) => send_msg(&res_tx_clone, Message::Failure(task, err)).await,
}
});
}
}
}
async fn start_loop_res(mut res_rx: Receiver<Message>, out_tx: Sender<String>){
loop {
if let Some(message) = res_rx.recv().await {
match message {
Message::Success(task, res) => {
send_msg(
&out_tx,
format!("{:#?}", res.text().await.unwrap()) // TODO: change in release!
).await;
},
Message::Failure(task, err) => {
send_msg(&out_tx, err.to_string()).await;
},
}
}
}
}
impl Proxy{
pub fn new(id: u32, parent_tx: Sender<String>, proxy_addr: &str, max_rps: u16, max_pending: u16) -> Result<Self, Error> {
let mut client = Client::builder();
if proxy_addr != "none" { client = client.proxy(reqwest::Proxy::all(proxy_addr)?) }
let (res_tx, res_rx) = mpsc::channel::<Message>(max_pending as usize + 1 as usize); // TODO: check size
let client = client.build()?;
let (in_tx, in_rx) = mpsc::channel::<Task>(max_pending as usize + 1 as usize);
let res_tx_clone = res_tx.clone();
tokio::spawn(async move { start_loop_in(client, in_rx, res_tx_clone).await });
tokio::spawn(async move { start_loop_res(res_rx, parent_tx).await });
Ok(Proxy{
id,
max_rps,
max_pending,
in_tx,
})
}
pub fn get_in_tx(&self) -> Sender<Task> {
self.in_tx.clone()
}
}