The following compiles and runs fine:
use chrono_tz::Tz;
use chrono::{TimeZone, NaiveDate};
use arrow2::temporal_conversions::parse_offset;
fn my_func(tz: &str) -> (){
let ndt = NaiveDate::from_ymd_opt(2018, 9, 28).unwrap().and_hms_opt(2, 30, 0).unwrap();
match parse_offset(&tz) {
Ok(time_zone) => {
println!("converted: {:?}", time_zone.from_utc_datetime(&ndt));
},
Err(_) => match tz.parse::<Tz>() {
Ok(time_zone) => {
println!("converted: {:?}", time_zone.from_utc_datetime(&ndt));
}
Err(error) => panic!("Problem opening the file: {:?}", error)
},
};
}
fn main() {
let time_zone = "Asia/Seoul";
my_func(&time_zone);
}
Noticed though that I'm doing
println!("converted: {:?}", time_zone.from_utc_datetime(&ndt));
twice.
I've tried refactoring to
use chrono_tz::Tz;
use chrono::{TimeZone, NaiveDate};
use arrow2::temporal_conversions::parse_offset;
fn my_func(tz: &str) -> (){
let ndt = NaiveDate::from_ymd_opt(2018, 9, 28).unwrap().and_hms_opt(2, 30, 0).unwrap();
let parsed_time_zone: TimeZone = match parse_offset(&tz) {
Ok(time_zone) => {
time_zone
},
Err(_) => match tz.parse::<Tz>() {
Ok(time_zone) => {
time_zone
}
Err(error) => panic!("Problem opening the file: {:?}", error)
},
};
println!("converted: {:?}", parsed_time_zone.from_utc_datetime(&ndt));
}
fn main() {
let time_zone = "Asia/Seoul";
my_func(&time_zone);
}
and get a long error:
error[E0782]: trait objects must include the `dyn` keyword
--> src/main.rs:7:27
|
7 | let parsed_time_zone: TimeZone = match parse_offset(&tz) {
| ^^^^^^^^
|
help: add `dyn` keyword before this trait
|
7 | let parsed_time_zone: dyn TimeZone = match parse_offset(&tz) {
| +++
error[E0191]: the value of the associated type `Offset` (from trait `TimeZone`) must be specified
--> src/main.rs:7:27
|
7 | let parsed_time_zone: TimeZone = match parse_offset(&tz) {
| ^^^^^^^^ help: specify the associated type: `TimeZone<Offset = Type>`
error[E0038]: the trait `TimeZone` cannot be made into an object
--> src/main.rs:7:27
|
7 | let parsed_time_zone: TimeZone = match parse_offset(&tz) {
| ^^^^^^^^ `TimeZone` cannot be made into an object
|
= note: the trait cannot be made into an object because it requires `Self: Sized`
= note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
error[E0308]: mismatched types
--> src/main.rs:9:13
|
9 | time_zone
| ^^^^^^^^^ expected trait object `dyn TimeZone`, found struct `FixedOffset`
|
= note: expected trait object `dyn TimeZone`
found struct `FixedOffset`
error[E0308]: mismatched types
--> src/main.rs:13:17
|
13 | time_zone
| ^^^^^^^^^ expected trait object `dyn TimeZone`, found enum `Tz`
|
= note: expected trait object `dyn TimeZone`
found enum `Tz`
error: the `from_utc_datetime` method cannot be invoked on a trait object
--> src/main.rs:18:50
|
18 | println!("converted: {:?}", parsed_time_zone.from_utc_datetime(&ndt));
| ^^^^^^^^^^^^^^^^^
|
::: /home/marcogorelli/.cargo/registry/src/github.com-1ecc6299db9ec823/chrono-0.4.23/src/offset/mod.rs:205:21
|
205 | pub trait TimeZone: Sized + Clone {
| ----- this has a `Sized` requirement
Some errors have detailed explanations: E0038, E0191, E0308, E0782.
For more information about an error, try `rustc --explain E0038`.
error: could not compile `tmp` due to 6 previous errors
I've tried the suggestion to do
let parsed_time_zone: dyn TimeZone = match parse_offset(&tz) {
but then it still errors with
error[E0191]: the value of the associated type `Offset` (from trait `TimeZone`) must be specified
--> src/main.rs:7:31
|
7 | let parsed_time_zone: dyn TimeZone = match parse_offset(&tz) {
| ^^^^^^^^ help: specify the associated type: `TimeZone<Offset = Type>`
How can I just parse tz as TimeZone, without specified whether it's Tz or FixedOffset, and then use it in the rest of the function?
You can't refactor this easily. Rust is strongly typed, and your two time_zone variables aren't the same type. One is chrono::FixedOffset, the other is chrono_tz::Tz.
If you do want to store both of them in the same variable, you have two possibilities:
trait object references (&dyn TimeZone)
Boxed trait objects (Box<dyn TimeZone).
dyn Timezone itself cannot directly be the type of a variable. It doesn't have a known size, because it could be any actual type of any size. Therefore you need one of the indirections above.
Both are non-ideal for your case though:
&dyn TimeZone won't work because someone has to own the actual object. You can't return a reference to a variable of an inner scope, as it gets dropped earlier than the reference does.
Box<dyn TimeZone> would work, no problem, but it introduces overhead: It will cause a heap allocation where the actual object will be put. The reason, as described earlier, is that a dyn object doesn't have a known size. Therefore, with Box, the actual object gets moved to the heap, and on the stack is the Box object, which has a known size.
So what I would do is: don't worry. The code duplication you have here is really minimal. I don't think it's worth the trouble.
Further detail I noticed after I wrote this answer:
TimeZone is : Sized, which means it isn't object safe and dyn TimeZone is disallowed entirely. So not even Box<dyn TimeZone> would work.
Related
I have a type Builder with a Generic Associated Type (GAT) InstanceForBuilder<'a>.
I'd like to write a function (build_with_42_for_bool<Builder>) that constraints the Builder to only those cases where Builder::InstanceForBuilder<'a>::InstanceProperty == bool (for all 'a).
I've been playing around for a while to get the syntax around this for <'a> right, but haven't been able to make this work.
The lifetime can't be a template argument of the function itself, because the reference only lives inside it.
Is this possible at all yet, given that GAT is an unstable feature?
playground
#![feature(generic_associated_types)]
// Trait definitions.
trait Builder {
type InstanceForBuilder<'a>: Instance<'a>;
fn build<'a>(&self, val: &'a usize) -> Self::InstanceForBuilder<'a>;
}
trait Instance<'a> {
// Some functions will only work when the instance has some concrete associated type.
type InstanceProperty;
}
fn build_with_42_for_bool<B: Builder>(builder: B)
where
// TODO: What do I put here to make this work?
for<'a> B::InstanceForBuilder<'a>: Instance<'a, InstanceProperty = bool>,
{
builder.build(&42);
// Do some testing here. The Instance won't be returned.
}
// Now try it out.
struct MyBuilder;
struct MyInstance<'a> {
val: &'a usize,
}
impl Builder for MyBuilder {
type InstanceForBuilder<'a> = MyInstance<'a>;
fn build<'a>(&self, val: &'a usize) -> Self::InstanceForBuilder<'a> {
MyInstance { val }
}
}
impl<'a> Instance<'a> for MyInstance<'a> {
type InstanceProperty = bool;
}
fn main() {
let builder = MyBuilder;
build_with_42_for_bool(builder); // TODO: Doesn't work
}
In my actual code, build_with_42_for_bool is a helper for testing that constructs the arguments passed to build in a specific way. For now I'll probably just inline that function everywhere, since the only problem is how to specify the lifetime of this one function. The code itself works fine.
Here's the full error:
Compiling pairwise-aligner v0.1.0 (/home/philae/git/eth/git/astar-pairwise-aligner)
error[E0271]: type mismatch resolving `for<'a> <<_ as Builder>::InstanceForBuilder<'a> as Instance<'a>>::InstanceProperty == bool`
--> examples/test.rs:45:5
|
45 | build_with_42(builder); // TODO: Doesn't work
| ^^^^^^^^^^^^^ expected `bool`, found associated type
|
= note: expected type `bool`
found associated type `<<_ as Builder>::InstanceForBuilder<'_> as Instance<'_>>::InstanceProperty`
= help: consider constraining the associated type `<<_ as Builder>::InstanceForBuilder<'_> as Instance<'_>>::InstanceProperty` to `bool`
= note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
note: required by a bound in `build_with_42`
--> examples/test.rs:19:53
|
16 | fn build_with_42<B: Builder>(builder: B)
| ------------- required by a bound in this
...
19 | for<'a> B::InstanceForBuilder<'a>: Instance<'a, InstanceProperty = bool>,
| ^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `build_with_42`
error[E0271]: type mismatch resolving `for<'a> <MyBuilder as Builder>::InstanceForBuilder<'a> == <MyBuilder as Builder>::InstanceForBuilder<'a>`
--> examples/test.rs:45:5
|
45 | build_with_42(builder); // TODO: Doesn't work
| ^^^^^^^^^^^^^ type mismatch resolving `for<'a> <MyBuilder as Builder>::InstanceForBuilder<'a> == <MyBuilder as Builder>::InstanceForBuilder<'a>`
|
note: expected this to be `<MyBuilder as Builder>::InstanceForBuilder<'a>`
--> examples/test.rs:32:35
|
32 | type InstanceForBuilder<'a> = MyInstance<'a>;
| ^^^^^^^^^^^^^^
= note: expected associated type `<MyBuilder as Builder>::InstanceForBuilder<'a>`
found struct `MyInstance<'a>`
help: a method is available that returns `<MyBuilder as Builder>::InstanceForBuilder<'a>`
--> examples/test.rs:8:5
|
8 | fn build<'a>(&self, val: &'a usize) -> Self::InstanceForBuilder<'a>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ consider calling `Builder::build`
note: required by a bound in `build_with_42`
--> examples/test.rs:19:40
|
16 | fn build_with_42<B: Builder>(builder: B)
| ------------- required by a bound in this
...
19 | for<'a> B::InstanceForBuilder<'a>: Instance<'a, InstanceProperty = bool>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `build_with_42`
For more information about this error, try `rustc --explain E0271`.
error: could not compile `pairwise-aligner` due to 2 previous errors
The correct syntax to do what you want is:
where B::InstanceForBuilder::InstanceProperty = bool
there's no need to introduce the for<'a> lifetime because the value of the associated type is not allowed to depend on the lifetime parameter. If you compile this code, you'll find that you get another error:
error: equality constraints are not yet supported in `where` clauses
--> src/main.rs:19:5
|
19 | B::InstanceForBuilder::InstanceProperty = bool,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ not supported
|
= note: see issue #20041 <https://github.com/rust-lang/rust/issues/20041> for more information
There's no immediate workaround for this. If you badly need this, you may be able to write a new trait, implement it only for bool, and then constrain things to that trait. That will get messy quickly though.
The answer from DreamConspiracy is a little outdated. I'd like to reuse the example codes from him to show a compiled solution
Instead of
where B::InstanceForBuilder::InstanceProperty = bool
You should write
fn build_with_42_for_bool_instance<'a, B, I>(builder: B)
where
B : Builder<InstanceForBuilder<'a>=I>,
I : Instance<'a, InstanceProperty=bool>,
{
builder.build(&42);
}
Following https://stackoverflow.com/a/65924807/5884503 I'm adding associated types so I can return futures from my trait methods:
pub trait RtspClient: Runnable {
type PlayResult: Future<Output = std::result::Result<(), ()>>;
type ConnectResult: Future<Output = std::result::Result<(), RtspConnectionError>>;
type DisconnectResult: Future<Output = std::result::Result<(), RtspDisconnectionError>>;
fn connect(&self) -> RtspClient::ConnectResult {
Ok(())
}
fn disconnect(&self) -> RtspClient::DisconnectResult {
Ok(())
}
}
However, the more methods I add, the more associated types I get.
This wouldn't be a problem, except that I need to specify all of them when I need to use dyn RtspClient:
|
12 | pub rtsp_client: Option<Arc<Mutex<dyn RtspClient>>>,
| ^^^^^^^^^^ help: specify the associated types: `RtspClient<PlayResult = Type, ConnectResult = Type, DisconnectResult = Type>`
|
Is there a way to force rust to use the default ones I already wrote in the trait definition?
There are multiple misunderstandings here. The Future<...> part of the associated type is not a default it is a constraint. Future is not a type, its a trait. Its saying an implementation's PlayResult, for example, must satisfy the Future<...> constraint.
A default associated type would look like this:
pub trait RtspClient {
type PlayResult = Ready<Result<(), ()>>; // using std::future::Ready as an example
}
But this syntax is not yet stabilized:
error[E0658]: associated type defaults are unstable
--> src/lib.rs:4:5
|
4 | type PlayResult = Ready<Result<(), ()>>; // using std::future::Ready as an example
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #29661 <https://github.com/rust-lang/rust/issues/29661> for more information
And even if you use a nightly compiler and enable the feature, you couldn't implement connect() and disconnect() by default because it must be valid for all possible implementations (you can do it in some cases, but I don't think this is one of them):
error[E0308]: mismatched types
--> src/lib.rs:13:9
|
12 | fn connect(&self) -> Self::ConnectResult {
| ------------------- expected `<Self as RtspClient>::ConnectResult` because of return type
13 | ready(Ok(()))
| ^^^^^^^^^^^^^ expected associated type, found struct `std::future::Ready`
|
= note: expected associated type `<Self as RtspClient>::ConnectResult`
found struct `std::future::Ready<Result<(), _>>`
error[E0308]: mismatched types
--> src/lib.rs:17:9
|
16 | fn disconnect(&self) -> Self::DisconnectResult {
| ---------------------- expected `<Self as RtspClient>::DisconnectResult` because of return type
17 | ready(Ok(()))
| ^^^^^^^^^^^^^ expected associated type, found struct `std::future::Ready`
|
= note: expected associated type `<Self as RtspClient>::DisconnectResult`
found struct `std::future::Ready<Result<(), _>>`
And even if that did work, you'll have problems using it as a trait object. Each implementation can have a different concrete associated types, but you need to specify the concrete associated types to use it as a trait object. They will undoubtedly not match:
struct MyDefaultClient;
impl RtspClient for MyDefaultClient {
// just uses the default types
fn connect(&self) -> Self::ConnectResult {
ready(Ok(()))
}
fn disconnect(&self) -> Self::DisconnectResult {
ready(Ok(()))
}
}
struct MyCustomClient;
impl RtspClient for MyCustomClient {
type ConnectResult = Pending<Result<(), RtspConnectionError>>; // different associated types
type DisconnectResult = Pending<Result<(), RtspDisconnectionError>>;
fn connect(&self) -> Self::ConnectResult {
pending()
}
fn disconnect(&self) -> Self::DisconnectResult {
pending()
}
}
fn main() {
let data = [
Box::new(MyDefaultClient) as Box<dyn RtspClient<ConnectResult = _, DisconnectResult = _>>,
Box::new(MyCustomClient),
];
}
The _ parts let the compiler infer the correct types, but will yield an error because dyn RtspClient<ConnectResult = A, ...> is different than dyn RtspClient<ConnectResult = B>.
error[E0271]: type mismatch resolving `<MyDefaultClient as RtspClient>::DisconnectResult == std::future::Pending<Result<(), RtspDisconnectionError>>`
--> src/main.rs:48:9
|
48 | Box::new(MyDefaultClient) as Box<dyn RtspClient<ConnectResult = _, DisconnectResult = _>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::future::Pending`, found struct `std::future::Ready`
|
= note: expected struct `std::future::Pending<Result<(), RtspDisconnectionError>>`
found struct `std::future::Ready<Result<(), RtspDisconnectionError>>`
= note: required for the cast to the object type `dyn RtspClient<DisconnectResult = std::future::Pending<Result<(), RtspDisconnectionError>>, ConnectResult = std::future::Pending<Result<(), RtspConnectionError>>>`
error[E0271]: type mismatch resolving `<MyDefaultClient as RtspClient>::ConnectResult == std::future::Pending<Result<(), RtspConnectionError>>`
--> src/main.rs:48:9
|
48 | Box::new(MyDefaultClient) as Box<dyn RtspClient<ConnectResult = _, DisconnectResult = _>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::future::Pending`, found struct `std::future::Ready`
|
= note: expected struct `std::future::Pending<Result<(), RtspConnectionError>>`
found struct `std::future::Ready<Result<(), RtspConnectionError>>`
= note: required for the cast to the object type `dyn RtspClient<DisconnectResult = std::future::Pending<Result<(), RtspDisconnectionError>>, ConnectResult = std::future::Pending<Result<(), RtspConnectionError>>>`
Associated types and trait objects do not mix in this fashion. You can think of them as regular generic types: Struct<A> is a completely different type from Struct<B>.
You should follow the other recommendations in the linked answer and use boxed futures, Pin<Box<Future<...>>>, or the helper crate async-trait instead of using associated types if you want to use different implementations interchangeably.
I have the following rust code cannot be compiled.
struct Person {
name : String,
age : u8,
}
fn main() {
let p = Person{ name: "Nobody".to_string(), age : 24};
let age = |p : &Person| p.age;
let name = |p : &Person | &p.name;
println! ("name={}, age={}" , name(&p), age(&p));
}
And the compiler gave the following error message.
Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
--> src/main.rs:11:31
|
11 | let name = |p : &Person | &p.name;
| ^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 11:16...
--> src/main.rs:11:16
|
11 | let name = |p : &Person | &p.name;
| ^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:11:31
|
11 | let name = |p : &Person | &p.name;
| ^^^^^^^
note: but, the lifetime must be valid for the expression at 2:29...
--> src/main.rs:13:5
|
13 | println! ("name={}, age={}" , name(&p), age(&p));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so type `(&&std::string::String, &u8)` of expression is valid during the expression
--> src/main.rs:13:5
|
13 | println! ("name={}, age={}" , name(&p), age(&p));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
error: aborting due to previous error
I tried to add the lifetime for name closure.
let name<'a> = |p : &'a Person | -> &'a String { &'a p.name };
but still got the compiler error
Compiling playground v0.0.1 (/playground)
error: expected one of `:`, `;`, `=`, `#`, or `|`, found `<`
--> src/main.rs:12:13
|
12 | let name<'a> = |p : &'a Person | -> &'a String { &'a p.name };
| ^ expected one of `:`, `;`, `=`, `#`, or `|`
error: aborting due to previous error
Just want to know how to write the correct code.
One other solution is to give an explicit type to your closure. Unfortunately, you can't use its actual type, but you can cast it to a function pointer.
Remember that the issue is that the compiler isn't able to correctly deduce that the lifetime of the output is tied to the lifetime of the input (it could be an instance of this bug, but I'm not at all sure). We can fix that by making the lifetimes explicit.
struct Person {
name: String,
age: u8,
}
fn main() {
let p = Person {
name: "Nobody".to_string(),
age: 24,
};
let age = |p: &Person| p.age;
// Our only changes are right here.
let name: for<'a> fn(&'a Person) -> &'a String = |p: &Person| &p.name;
println!("name={}, age={}", name(&p), age(&p));
}
(playground)
In fact, it's possible to be slightly less explicit than this. The compiler is fine figuring out the types of the input and output. It's just the lifetimes it has trouble with. So replacing that line with let name: for<'a> fn(&'a _) -> &'a _ = |p: &Person| &p.name; also works (playground).
For me it is more important to understand the source of the problem that find a workaround,
so step by step. let's start from something that works:
struct Person {
name: String,
age: u8,
}
fn get_name<'a>(person: &'a Person) -> &'a str {
&person.name
}
fn main() {
let p = Person {
name: "Nobody".to_string(),
age: 24,
};
let age = |p: &Person| p.age;
let name = get_name;
println!("name={}, age={}", name(&p), age(&p));
}
There is no issues when using a function instead of a clousure. In this case,
the compiler is able to check that lifetime requirements are ok.
But when trying to use a closure for name:
let name = |p : &Person | &p.name;
You get the cannot infer an appropriate lifetime error.
Why?
A closure captures its environment: some opaque struct has to be created
by the compiler and such struct has to be callable.
I'm non fully aware of the internal details, but something along these lines would be created when desugaring your closure:
struct OpaqueType<'a> {
// a PhantomData because you don't capure nothing
// just to make explicit that struct lifetime bind to environment
// if you would had captured some integer:
// captured_int: &'a i32,
captured_int: PhantomData<&'a i32>,
}
impl<'a> OpaqueType<'a> {
fn call<'b>(&'b self, person: &'a Person) -> &'a str {
&person.name
}
}
And looking at call it is apparent that when a clusure argument is a reference there are two unrelated lifetimes at play.
Finally the answer: how to return a reference
Also note that in your case, not declaring the argument type and using the helper function get_name, works:
// let name = |p| &p.name; // does not work, not enough info to infer p type
let name = |p| get_name(p);
My guess is that in such case the compiler, following some inference path, is able to desugar in a way that lifetimes are bounded as expected.
I'm trying to match on the datatype of a generic field of a struct and react accordingly. My general idea was like this (code doesn't compile):
struct Foo<T> {
bar: T,
}
fn main() {
let x = Foo::<String> {
bar: "world".to_string(),
};
match x.bar {
String => println!("It's a string!"),
u32 => println!("It's a u32!"),
_ => println!("Something else"),
};
println!("end of program!");
}
The error message from the compiler:
warning: unreachable pattern
--> src/main.rs:12:9
|
11 | String => println!("It's a string!"),
| ------ matches any value
12 | u32 => println!("It's a u32!"),
| ^^^ unreachable pattern
|
= note: `#[warn(unreachable_patterns)]` on by default
warning: unreachable pattern
--> src/main.rs:13:9
|
11 | String => println!("It's a string!"),
| ------ matches any value
12 | u32 => println!("It's a u32!"),
13 | _ => println!("Something else"),
| ^ unreachable pattern
warning: unused variable: `String`
--> src/main.rs:11:9
|
11 | String => println!("It's a string!"),
| ^^^^^^ help: consider prefixing with an underscore: `_String`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `u32`
--> src/main.rs:12:9
|
12 | u32 => println!("It's a u32!"),
| ^^^ help: consider prefixing with an underscore: `_u32`
warning: variable `String` should have a snake case name
--> src/main.rs:11:9
|
11 | String => println!("It's a string!"),
| ^^^^^^ help: convert the identifier to snake case: `string`
|
= note: `#[warn(non_snake_case)]` on by default
What I wanted was for x to match the first one. I'm actually not sure what I want to do can be done, but what would achieve the desired effect?
Idiomatic Solution
Create a trait which constrains the parameter T in Foo, implement any specific behavior as an associated function of this trait.
Example:
trait PrintMe {
fn print_me(&self);
}
impl PrintMe for String {
fn print_me(&self) { println!("I am a string"); }
}
struct Foo<T: PrintMe> {
bar: T
}
fn main() {
// ...
x.bar.print_me();
}
This is principled generic programming, where you declare exactly the difference of behavior of the possible generic parameters, so that there is no surprise.
See also:
Why is this match pattern unreachable when using non-literal patterns?
Exact Solution
Rust can indeed query types: each type has a unique TypeId associated, and you can match on TypeId with a series of if checks. It's clunky.
fn print_me<T>(x: &Foo<T>) {
if TypeId::of::<T>() == TypeId::of::<String>() {
println!("I am a string");
} else // ...
}
But please... don't do that :)
A more general look at matching over types
Basically, you hardly can match on the type, instead you have to use rust's traits.
Here is a helpful introduction for Rust's traits:
https://doc.rust-lang.org/rust-by-example/trait.html
I will explain simply how to match over type so it's okay if you don't know how to use traits.
Here is how to more generally change behavior depending on the type:
Define a trait that is going to do something depending on the type.
Add a function that takes in what you need to do something with it.
It will take the variable with a type that varies as self. Don't add a body to that function, just follow the definition with a semicolon ;.
trait DoSomething {
fn someFunction(&self);
}
Implement the trait for all the types you wanted to match for: String and u32.
This is the part where you can code different behavior for each type.
impl DoSomething for String {
fn someFunction(&self) {
println!("It is a string!")
}
}
impl DoSomething for u32 {
fn someFunction(&self) {
println!("It is a u32!")
}
}
Instead of matching on an element, call the method in the trait on it.
struct Foo<T> {
bar: T,
}
fn main() {
let x = Foo::<String> {
bar: "world".to_string(),
};
// the call of the method someFunction will differentiate the type of x.bar
x.bar.someFunction();
}
TLDR: in Rust, to match over type, we create a trait, implement a function for each type and call it on the element to match.
I'm trying to match on the datatype of a generic field of a struct and react accordingly. My general idea was like this (code doesn't compile):
struct Foo<T> {
bar: T,
}
fn main() {
let x = Foo::<String> {
bar: "world".to_string(),
};
match x.bar {
String => println!("It's a string!"),
u32 => println!("It's a u32!"),
_ => println!("Something else"),
};
println!("end of program!");
}
The error message from the compiler:
warning: unreachable pattern
--> src/main.rs:12:9
|
11 | String => println!("It's a string!"),
| ------ matches any value
12 | u32 => println!("It's a u32!"),
| ^^^ unreachable pattern
|
= note: `#[warn(unreachable_patterns)]` on by default
warning: unreachable pattern
--> src/main.rs:13:9
|
11 | String => println!("It's a string!"),
| ------ matches any value
12 | u32 => println!("It's a u32!"),
13 | _ => println!("Something else"),
| ^ unreachable pattern
warning: unused variable: `String`
--> src/main.rs:11:9
|
11 | String => println!("It's a string!"),
| ^^^^^^ help: consider prefixing with an underscore: `_String`
|
= note: `#[warn(unused_variables)]` on by default
warning: unused variable: `u32`
--> src/main.rs:12:9
|
12 | u32 => println!("It's a u32!"),
| ^^^ help: consider prefixing with an underscore: `_u32`
warning: variable `String` should have a snake case name
--> src/main.rs:11:9
|
11 | String => println!("It's a string!"),
| ^^^^^^ help: convert the identifier to snake case: `string`
|
= note: `#[warn(non_snake_case)]` on by default
What I wanted was for x to match the first one. I'm actually not sure what I want to do can be done, but what would achieve the desired effect?
Idiomatic Solution
Create a trait which constrains the parameter T in Foo, implement any specific behavior as an associated function of this trait.
Example:
trait PrintMe {
fn print_me(&self);
}
impl PrintMe for String {
fn print_me(&self) { println!("I am a string"); }
}
struct Foo<T: PrintMe> {
bar: T
}
fn main() {
// ...
x.bar.print_me();
}
This is principled generic programming, where you declare exactly the difference of behavior of the possible generic parameters, so that there is no surprise.
See also:
Why is this match pattern unreachable when using non-literal patterns?
Exact Solution
Rust can indeed query types: each type has a unique TypeId associated, and you can match on TypeId with a series of if checks. It's clunky.
fn print_me<T>(x: &Foo<T>) {
if TypeId::of::<T>() == TypeId::of::<String>() {
println!("I am a string");
} else // ...
}
But please... don't do that :)
A more general look at matching over types
Basically, you hardly can match on the type, instead you have to use rust's traits.
Here is a helpful introduction for Rust's traits:
https://doc.rust-lang.org/rust-by-example/trait.html
I will explain simply how to match over type so it's okay if you don't know how to use traits.
Here is how to more generally change behavior depending on the type:
Define a trait that is going to do something depending on the type.
Add a function that takes in what you need to do something with it.
It will take the variable with a type that varies as self. Don't add a body to that function, just follow the definition with a semicolon ;.
trait DoSomething {
fn someFunction(&self);
}
Implement the trait for all the types you wanted to match for: String and u32.
This is the part where you can code different behavior for each type.
impl DoSomething for String {
fn someFunction(&self) {
println!("It is a string!")
}
}
impl DoSomething for u32 {
fn someFunction(&self) {
println!("It is a u32!")
}
}
Instead of matching on an element, call the method in the trait on it.
struct Foo<T> {
bar: T,
}
fn main() {
let x = Foo::<String> {
bar: "world".to_string(),
};
// the call of the method someFunction will differentiate the type of x.bar
x.bar.someFunction();
}
TLDR: in Rust, to match over type, we create a trait, implement a function for each type and call it on the element to match.