I'm starting to get comfortable with Rust, but there are still some things that are really tripping me up with lifetimes. In this particular case, what I want to do is have an enum which may have different types wrapped as a generic parameter class to create strongly typed query parameters in a URL, though the specific use case is irrelevant, and return a conversion of that wrapped value into an &str. Here's an example of what I want to do:
enum Param<'a> {
MyBool(bool),
MyLong(i64),
MyStr(&'a str),
}
impl<'a> Param<'a> {
fn into(self) -> (&'static str, &'a str) {
match self {
Param::MyBool(b) => ("my_bool", &b.to_string()), // clearly wrong
Param::MyLong(i) => ("my_long", &i.to_string()), // clearly wrong
Param::Value(s) => ("my_str", s),
}
}
}
What I ended up doing is this to deal with the obvious lifetime issue (and yes, it's obvious to me why the lifetime isn't long enough for the into() function):
enum Param<'a> {
MyBool(&'a str), // no more static typing :(
MyLong(&'a str), // no more static typing :(
MyStr(&'a str),
}
impl<'a> Param<'a> {
fn into(self) -> (&'static str, &'a str) {
match self {
Param::MyBool(b) => ("my_bool", b),
Param::MyLong(i) => ("my_long", i),
Param::Value(s) => ("my_str", s),
}
}
}
This seems like an ugly workaround in a case where what I really want to do is guarantee the static typing of certain params, b/c now it's the constructor of the enum that's responsible for the proper type conversion. Curious if there is a way to do this... and yes, at some point I need &str as that is a parameter elsewhere, specifically:
let body = url::form_urlencoded::serialize(
vec![Param::MyBool(&true.to_string()).
into()].
into_iter());
I went through a whole bunch of things like trying to return String instead of &str from into(), but that only caused conversion issues down the line with a map() of String -> &str. Having the tuple correct from the start is the easiest thing, rather than fighting the compiler at every turn after that.
-- update--
Ok, so I went back to a (String,String) tuple in the into() function for the enum. It turns out that there is an "owned" version of the url::form_urlencoded::serialize() function which this is compatible with.
pub fn serialize_owned(pairs: &[(String, String)]) -> String
But, now I'm also trying to use the same pattern for the query string in the hyper::URL, specifically:
fn set_query_from_pairs<'a, I>(&mut self, pairs: I)
where I: Iterator<Item=(&'a str, &'a str)>
and then I try to use map() on the iterator that I have from the (String,String) tuple:
params: Iterator<Item=(String, String)>
url.set_query_from_pairs(params.map(|x: (String, String)| ->
(&str, &str) { let (ref k, ref v) = x; (k, v) } ));
But this gets error: x.0 does not live long enough. Ref seems correct in this case, right? If I don't use ref, then it's k/v that don't live long enough. Is there something 'simple' that I'm missing in this?
It is not really clear why you can't do this:
enum Param<'a> {
MyBool(bool),
MyLong(i64),
MyStr(&'a str),
}
impl<'a> Param<'a> {
fn into(self) -> (&'static str, String) {
match self {
Param::MyBool(b) => ("my_bool", b.to_string()),
Param::MyLong(i) => ("my_long", i.to_string()),
Param::MyStr(s) => ("my_str", s.into()),
}
}
}
(into() for &str -> String conversion is slightly more efficient than to_string())
You can always get a &str from String, e.g. with deref coercion or explicit slicing.
Related
I want to specialize &'static str from &'a str:
use std::borrow::Cow;
struct MyString {
inner: Cow<'static, str>,
}
impl From<&'static str> for MyString {
fn from(x: &'static str) -> Self {
MyString {
inner: Cow::Borrowed(x),
}
}
}
impl<T: Into<String>> From<T> for MyString {
fn from(x: T) -> Self {
MyString {
inner: Cow::Owned(x.into()),
}
}
}
fn main() {
match MyString::from("foo").inner {
Cow::Borrowed(..) => (),
_ => {
panic!();
}
}
let s = String::from("bar");
match MyString::from(s.as_ref()).inner {
Cow::Owned(..) => (),
_ => {
panic!();
}
}
match MyString::from(String::from("qux")).inner {
Cow::Owned(..) => (),
_ => {
panic!();
}
}
}
The gist is that MyString stores a statically-allocated string literal as a &'static str and all other strings as a String. This allows MyString to avoid having a lifetime parameter—i.e., MyString<'a>, which is critical for my API, all while allowing the caller to pass in any kind of string and have MyString automatically do the correct thing.
The problem is that the code doesn't compile:
error[E0119]: conflicting implementations of trait `std::convert::From<&'static str>` for type `MyString`:
--> src/main.rs:15:1
|
7 | impl From<&'static str> for MyString {
| ------------------------------------ first implementation here
...
15 | impl<T: Into<String>> From<T> for MyString {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyString`
Is there any trick that allows me to do what I want? If not, is lifetime specialization something that Rust will ever support?
Rust 1.51.0 does not have specialization of any kind. If I'm reading the specialization RFC correctly, then lifetime specialization will not be supported even when the RFC is implemented:
A hard constraint in the design of the trait system is that dispatch
cannot depend on lifetime information. In particular, we both cannot,
and should not allow specialization based on lifetimes:
We can't, because when the compiler goes to actually generate code ("trans"), lifetime information has been erased -- so we'd have no
idea what specializations would soundly apply.
We shouldn't, because lifetime inference is subtle and would often lead to counterintuitive results. For example, you could easily fail
to get 'static even if it applies, because inference is choosing the
smallest lifetime that matches the other constraints.
(Emphasis mine)
There's some examples further in the link that indicate some of the concrete issues.
I recommend using a Cow to handle the "owned or borrowed" case.
I write this answer after reading this duplicated post asking how to define a method/function that behaves differently when it is passed a static string or a non-static string.
This is not possible, so a workaround may be using a wrapper type to wrap the string argument in an enum:
enum MyString {
Static(&'static str),
Heap(String),
}
fn bar(arg: &MyString) {
match arg {
&MyString::Static(ref name) => println!("my first pc was {}", name),
&MyString::Heap(ref name) => println!("I dont know {}", name),
}
}
fn main() {
let mut v = Vec::new();
let forever: &'static str = "zx-spectrum";
let local: &str = &"commodore64".to_string();
v.push(MyString::Static(forever));
// ERROR: try to insert 'a lifetime
// v.push(Mystring::Static(local));
v.push(MyString::Heap(local.to_string()));
v.push(MyString::Heap("muggle".to_string()));
bar(&v[0]);
bar(&v[1]);
}
MyString stores a statically-allocated string literal as a &'static str and all other strings as a String.
As pointed in the comments below, the standard library provides a type that fits the borrowed/owned case: the smart pointer Cow.
The enum MyString used in this example is just a specific enum for managing string types.
The only difference stems from a somewhat more specific naming of the enum and its variants related to the specific usage: MyString::Static("forever") versus Cow::Borrowed("forever") and MyString::Heap(str) versus Cow::Owned(str).
Does this help improve mnemonics and code readability? I'm quite sure that this holds only for novices or occasional Rust programmers, not for seasoned Rustaceans.
I want to specialize &'static str from &'a str:
use std::borrow::Cow;
struct MyString {
inner: Cow<'static, str>,
}
impl From<&'static str> for MyString {
fn from(x: &'static str) -> Self {
MyString {
inner: Cow::Borrowed(x),
}
}
}
impl<T: Into<String>> From<T> for MyString {
fn from(x: T) -> Self {
MyString {
inner: Cow::Owned(x.into()),
}
}
}
fn main() {
match MyString::from("foo").inner {
Cow::Borrowed(..) => (),
_ => {
panic!();
}
}
let s = String::from("bar");
match MyString::from(s.as_ref()).inner {
Cow::Owned(..) => (),
_ => {
panic!();
}
}
match MyString::from(String::from("qux")).inner {
Cow::Owned(..) => (),
_ => {
panic!();
}
}
}
The gist is that MyString stores a statically-allocated string literal as a &'static str and all other strings as a String. This allows MyString to avoid having a lifetime parameter—i.e., MyString<'a>, which is critical for my API, all while allowing the caller to pass in any kind of string and have MyString automatically do the correct thing.
The problem is that the code doesn't compile:
error[E0119]: conflicting implementations of trait `std::convert::From<&'static str>` for type `MyString`:
--> src/main.rs:15:1
|
7 | impl From<&'static str> for MyString {
| ------------------------------------ first implementation here
...
15 | impl<T: Into<String>> From<T> for MyString {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyString`
Is there any trick that allows me to do what I want? If not, is lifetime specialization something that Rust will ever support?
Rust 1.51.0 does not have specialization of any kind. If I'm reading the specialization RFC correctly, then lifetime specialization will not be supported even when the RFC is implemented:
A hard constraint in the design of the trait system is that dispatch
cannot depend on lifetime information. In particular, we both cannot,
and should not allow specialization based on lifetimes:
We can't, because when the compiler goes to actually generate code ("trans"), lifetime information has been erased -- so we'd have no
idea what specializations would soundly apply.
We shouldn't, because lifetime inference is subtle and would often lead to counterintuitive results. For example, you could easily fail
to get 'static even if it applies, because inference is choosing the
smallest lifetime that matches the other constraints.
(Emphasis mine)
There's some examples further in the link that indicate some of the concrete issues.
I recommend using a Cow to handle the "owned or borrowed" case.
I write this answer after reading this duplicated post asking how to define a method/function that behaves differently when it is passed a static string or a non-static string.
This is not possible, so a workaround may be using a wrapper type to wrap the string argument in an enum:
enum MyString {
Static(&'static str),
Heap(String),
}
fn bar(arg: &MyString) {
match arg {
&MyString::Static(ref name) => println!("my first pc was {}", name),
&MyString::Heap(ref name) => println!("I dont know {}", name),
}
}
fn main() {
let mut v = Vec::new();
let forever: &'static str = "zx-spectrum";
let local: &str = &"commodore64".to_string();
v.push(MyString::Static(forever));
// ERROR: try to insert 'a lifetime
// v.push(Mystring::Static(local));
v.push(MyString::Heap(local.to_string()));
v.push(MyString::Heap("muggle".to_string()));
bar(&v[0]);
bar(&v[1]);
}
MyString stores a statically-allocated string literal as a &'static str and all other strings as a String.
As pointed in the comments below, the standard library provides a type that fits the borrowed/owned case: the smart pointer Cow.
The enum MyString used in this example is just a specific enum for managing string types.
The only difference stems from a somewhat more specific naming of the enum and its variants related to the specific usage: MyString::Static("forever") versus Cow::Borrowed("forever") and MyString::Heap(str) versus Cow::Owned(str).
Does this help improve mnemonics and code readability? I'm quite sure that this holds only for novices or occasional Rust programmers, not for seasoned Rustaceans.
I want my getv() function return the value from a HashMap. I get errors no matter how I modify my code:
enum Type {
TyInt,
TyBool,
}
struct TypeEnv {
Env: HashMap<char, Type>,
}
impl TypeEnv {
fn set(&mut self, name: char, ty: Type) {
self.Env.insert(name, ty);
}
fn getv(self, name: char) -> Type {
match self.Env.get(&name) {
Some(Type) => Type, // <------- Always error here
None => Type::TyInt,
}
}
}
HashMap::get returns an Option<&Type>, not an Option<Type>, which is why just returning the matched value fails to compile.
get provides a reference because in Rust you cannot simply return the actual value that is in the hash table - you need to either clone it (make a copy, potentially expensive), remove it from the container and transfer it to the caller, or return a reference to the value inside. HashMap::get chooses the last option because it is cheap while providing the greatest flexibility to the caller, which can choose to either inspect the value or clone it.
For your enum that consists of a single machine word, copying the value would be the optimal approach. (For more complex enums, a better approach is to return a reference as shown in Simon's answer.) Copying requires:
making Type copyable, by marking it with the Copy trait using the #[derive(Copy, Clone)] directive at the type definition.
returning the copy from getv by dereferencing the matched value using *matched_value, or by using & in the pattern.
Finally, your getv method consumes the object, which means that you can call it only once. This is almost certainly not what was intended - it should accept &self instead. With those changes, the resulting code looks like this:
use std::collections::HashMap;
#[derive(Copy, Clone)]
enum Type {
TyInt,
TyBool,
}
struct TypeEnv {
env: HashMap<char, Type>,
}
impl TypeEnv {
fn set(&mut self, name: char, ty: Type) {
self.env.insert(name, ty);
}
fn getv(&self, name: char) -> Type {
match self.env.get(&name) {
Some(&v) => v,
None => Type::TyInt,
}
}
}
If the type needs to contain non-Copy data, then you can make it only Clone instead, and return v.clone().
You don't have to make your type Copy or Clone ... if you're happy working with the reference.
When things like this pop up while writing Rust, its generally best to re-evaluate how you're calling the code. Can it be made simpler?
You can either have the caller expect the Option<&V>, or just force something out... perhaps like this:
fn getv(&self, name: char) -> &Type {
self.env.get(&name).unwrap_or(&Type::TyInt)
}
This simplifies your code and makes it pretty clear what you're after. Here it is in action:
use std::collections::HashMap;
#[derive(Debug)]
enum Type {
TyInt,
TyBool,
}
struct TypeEnv {
env: HashMap<char, Type>,
}
impl TypeEnv {
fn set(&mut self, name: char, ty: Type) {
self.env.insert(name, ty);
}
fn getv(&self, name: char) -> &Type {
self.env.get(&name).unwrap_or(&Type::TyInt)
}
}
fn main() {
let mut te = TypeEnv {
env: HashMap::new(),
};
{
let arg = te.getv('c');
println!("{:?}", arg); // "TyInt" - because nothing was found
}
te.set('c', Type::TyBool);
let arg = te.getv('c'); // "TyBool" - because we stored it in the line above.
println!("{:?}", arg);
}
Also, your existing implementation of getv takes ownership of self ... notice how I added the reference getv(&self). If you don't have this, this type (as far as I can tell) becomes basically useless.
I want to specialize &'static str from &'a str:
use std::borrow::Cow;
struct MyString {
inner: Cow<'static, str>,
}
impl From<&'static str> for MyString {
fn from(x: &'static str) -> Self {
MyString {
inner: Cow::Borrowed(x),
}
}
}
impl<T: Into<String>> From<T> for MyString {
fn from(x: T) -> Self {
MyString {
inner: Cow::Owned(x.into()),
}
}
}
fn main() {
match MyString::from("foo").inner {
Cow::Borrowed(..) => (),
_ => {
panic!();
}
}
let s = String::from("bar");
match MyString::from(s.as_ref()).inner {
Cow::Owned(..) => (),
_ => {
panic!();
}
}
match MyString::from(String::from("qux")).inner {
Cow::Owned(..) => (),
_ => {
panic!();
}
}
}
The gist is that MyString stores a statically-allocated string literal as a &'static str and all other strings as a String. This allows MyString to avoid having a lifetime parameter—i.e., MyString<'a>, which is critical for my API, all while allowing the caller to pass in any kind of string and have MyString automatically do the correct thing.
The problem is that the code doesn't compile:
error[E0119]: conflicting implementations of trait `std::convert::From<&'static str>` for type `MyString`:
--> src/main.rs:15:1
|
7 | impl From<&'static str> for MyString {
| ------------------------------------ first implementation here
...
15 | impl<T: Into<String>> From<T> for MyString {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyString`
Is there any trick that allows me to do what I want? If not, is lifetime specialization something that Rust will ever support?
Rust 1.51.0 does not have specialization of any kind. If I'm reading the specialization RFC correctly, then lifetime specialization will not be supported even when the RFC is implemented:
A hard constraint in the design of the trait system is that dispatch
cannot depend on lifetime information. In particular, we both cannot,
and should not allow specialization based on lifetimes:
We can't, because when the compiler goes to actually generate code ("trans"), lifetime information has been erased -- so we'd have no
idea what specializations would soundly apply.
We shouldn't, because lifetime inference is subtle and would often lead to counterintuitive results. For example, you could easily fail
to get 'static even if it applies, because inference is choosing the
smallest lifetime that matches the other constraints.
(Emphasis mine)
There's some examples further in the link that indicate some of the concrete issues.
I recommend using a Cow to handle the "owned or borrowed" case.
I write this answer after reading this duplicated post asking how to define a method/function that behaves differently when it is passed a static string or a non-static string.
This is not possible, so a workaround may be using a wrapper type to wrap the string argument in an enum:
enum MyString {
Static(&'static str),
Heap(String),
}
fn bar(arg: &MyString) {
match arg {
&MyString::Static(ref name) => println!("my first pc was {}", name),
&MyString::Heap(ref name) => println!("I dont know {}", name),
}
}
fn main() {
let mut v = Vec::new();
let forever: &'static str = "zx-spectrum";
let local: &str = &"commodore64".to_string();
v.push(MyString::Static(forever));
// ERROR: try to insert 'a lifetime
// v.push(Mystring::Static(local));
v.push(MyString::Heap(local.to_string()));
v.push(MyString::Heap("muggle".to_string()));
bar(&v[0]);
bar(&v[1]);
}
MyString stores a statically-allocated string literal as a &'static str and all other strings as a String.
As pointed in the comments below, the standard library provides a type that fits the borrowed/owned case: the smart pointer Cow.
The enum MyString used in this example is just a specific enum for managing string types.
The only difference stems from a somewhat more specific naming of the enum and its variants related to the specific usage: MyString::Static("forever") versus Cow::Borrowed("forever") and MyString::Heap(str) versus Cow::Owned(str).
Does this help improve mnemonics and code readability? I'm quite sure that this holds only for novices or occasional Rust programmers, not for seasoned Rustaceans.
I'm having this error I can't seem to understand when dealing with match in a function, so currently I have of string tuples: let mut bitmap_point: Vec<(&str, &str)> = vec![]; and I want to use this in a function convert_to_binary, so I declared this:
fn convert_to_binary_string(tup: &(&str,&str) ) -> String{
let mut stringval: String =
match tup {
&(x,y) => from_str(x)
};
return stringval;
}
however upon calling the function with
let h = convert_to_binary_string( bitmap_point.get(0) );
I get the error :
>rustc main.rs
main.rs:12:3: 14:4 error: mismatched types: expected `collections::string::String` but found `core::option::Option<<generic #5>>` (expected struct collections::string::String but found enum core::option::Option)
main.rs:12 match tup {
main.rs:13 &(x,y) => from_str(x)
main.rs:14 };
main.rs:13:15: 13:23 error: cannot determine a type for this bounded type parame
ter: unconstrained type
main.rs:13 &(x,y) => from_str(x)
^~~~~~~~
could anyone explain what I'm going wrong?
You’re going about this the wrong way. If you merely want to convert a &str (a string slice) into a String (an owned strng), you should use the .to_string() method or String::from_str(str).
The FromStr trait is based around the Option type for conversions where not all inputs have a valid output—e.g. from_str::<int>("four") will return None because it can only cope with number literals. Where you’re turning something into a string, there are no failure cases, and from_str(str).unwrap() is a messy way of doing it.
Here is more idiomatic code to achieve your goal (using ref1()):
fn convert_to_binary_string(tup: &(&str, &str)) -> String {
tup.ref1().to_string()
}
This could easily be rewritten without ref1() using matching, as you have done, or a simple let binding:
fn convert_to_binary_string(tup: &(&str, &str)) -> String {
let &(x, _) = tup;
x.to_string()
}
But as a general rule, you should probably not be using tuples anyway—it’s normally a better idea to use a structure with meaningfully named fields, e.g.
struct Foo<'a> {
x: &'a str,
y: &'a str,
}
… then it would have been
fn convert_to_binary_string(foo: &Foo) -> String {
foo.x.to_string()
}
And at that stage you can probably drop the method altogether.
Ok so the answer just took a simple documentation search and I found the answer, my issue was that from_str(&str) return -> Option<T> where the value could either be Some or None. This was why I was receiving the Option error as shown above.
To fix this I checkout out rust's documentation and found a function unwrap() which could pull T out of the option variable. Everything works as expected and my final function is:
fn convert_to_binary_string(tup: &(&str,&str) ) -> String{
let mut stringval: String =
match tup {
&(x,y) => from_str(x).unwrap()
};
return stringval;
}