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.
Related
Assuming the following code is present
use core::any::Any;
enum Value {
Any(Box<dyn Any>),
Other, // placeholder, this code is adapted from mine
}
This code raises a diagnostic that I can't quite understand
impl<T: Any> TryFrom<Value> for T {
type Error = &'static str;
fn try_from(val: Value) -> Result<Self, Self::Error> {
if let Value::Any(any) = val {
if let Ok(down) = any.downcast::<T>() {
Ok(*down)
} else {
Err("incorrect type")
}
} else { Err("not an any") }
}
}
fn main() {
let res: Result<usize, &'static str> = Value::Any(Box::new(1usize)).try_into();
dbg!(res);
}
error[E0210]: type parameter `T` must be covered by another type when it appears before the first local type (`Value`)
--> src/main.rs:9:6
|
9 | impl<T: Any> TryFrom<Value> for T {
| ^ type parameter `T` must be covered by another type when it appears before the first local type (`Value`)
|
= note: implementing a foreign trait is only possible if at least one of the types for which it is implemented is local, and no uncovered type parameters appear before that first local type
= note: in this case, 'before' refers to the following order: `impl<..> ForeignTrait<T1, ..., Tn> for T0`, where `T0` is the first and `Tn` is the last
I still don't quite understand what "must be covered by another type" means, nor "when it appears before the first local type".
However, if I modify the impl signature to target a single-element tuple containing T, the impl does not raise an error, and the code functions correctly:
impl<T: Any> TryFrom<Value> for (T,) {
type Error = &'static str;
fn try_from(val: Value) -> Result<Self, Self::Error> {
if let Value::Any(any) = val {
if let Ok(down) = any.downcast::<T>() {
Ok((*down,))
} else {
Err("incorrect type")
}
} else { Err("not an any") }
}
}
fn main() {
let res: Result<(usize,), &'static str> = Value::Any(Box::new(1usize)).try_into();
dbg!(res);
}
What purpose does the single-element tuple actually serve?
(Playground Link)
From RFC 2451:
Covered Type: A type which appears as a parameter to another type. For example, T is uncovered, but the T in Vec<T> is covered. This is only relevant for type parameters.
It is important to note that the type T does not equal the tuple type (T,). (T,) can be considered equivalent to a hypothetical generic newtype/tuple struct struct Tuple1<T>(T) defined in the standard library crate std. With this analogy, impl<T: Any> TryFrom<Value> for (T,) is equivalent to impl<T: Any> TryFrom<Value> for std::Tuple1<T>.
Note that the covering type (in this case the single element tuple type, or in our analogy Tuple1) need not be defined locally in the same crate. To put it simply, consider an impl<T> ForeignTrait<LocalType> for ForeignType<T>:
The covering type ForeignType has already been defined. So:
The only way ForeignTrait<LocalType> can be implemented for
ForeignType<T> outside of the current crate is through a generic
impl <S, T> ForeignTrait<S> for ForeignType<T> (where S covers
LocalType).
Because of these rules, an impl <S, T> ForeignTrait<S> for ForeignType<T> that covers ForeignTrait<LocalType> is only possible in the crate declaring ForeignType.
Hence it is impossible for a conflicting implementation of ForeignTrait<LocalType> to exist for ForeignType<T> outside of a) the local crate and b) the crate declaring ForeignType, and so the impl is allowed. The RFC discusses this in more detail.
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 am a Rust beginner and I can't solve this type problem. I have tried replacing &name with name, but the error "pattern &_ not covered" occurred.
fn get_project(name: &'static str) {
match &name {
"hi" => {},
}
}
fn main() {
let project = get_project("hi");
}
Compiler error:
error[E0308]: mismatched types
--> <anon>:3:9
|
3 | "hi" => {},
| ^^^^ expected &str, found str
|
= note: expected type `&&str`
= note: found type `&'static str`
String literals – like "hi" – have the type &'static str. So if you already have a &str, you don't need to add the &:
fn get_project(name: &str) {
match name {
"hi" => {},
_ => {}, // matches have to be exhaustive
}
}
I also added a default case, because matches in Rust need to be exhaustive: they need to cover all possible cases.
Maybe you noticed, that I also removed the 'static from the argument list. If you want to read about some lifetime stuff, go ahead. Else, stop reading here, because it's possibly confusing and not that important in this case.
In this function there is no need to restrict the lifetime of the given argument to 'static. Maybe you also want to pass in string slices that are borrowed from a String:
let user_input = read_user_input(); // type `String`
get_project(&input);
The code above only works when you remove the 'static from the argument. Once removed, the function is equivalent to:
fn get_project<'a>(name: &'a str) { ... }
This means that the function is generic over a lifetime 'a. The function says: given any lifetime 'a, you can give me a string with said lifetime and I am able to do my thing. Which is true. If the function wouldn't be able to do it for any lifetime, the compiler would complain ;-)
In your example, name doesn't need to have a static lifetime. Because you only use name inside your function, name doesn't need to have an extended lifetime. Check out the strings chapter of The Rust Programming Language. To match a &str with a &'static str you don't need &, just the variable itself is enough.
pub fn get_project(name: &str) {
match name {
"hi" => println!("I found hi!"),
_ => println!("Nothing match"),
}
}
fn main() {
get_project("hi");
get_project("42");
}
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 have a program that involves examining a complex data structure to see if it has any defects. (It's quite complicated, so I'm posting example code.) All of the checks are unrelated to each other, and will all have their own modules and tests.
More importantly, each check has its own error type that contains different information about how the check failed for each number. I'm doing it this way instead of just returning an error string so I can test the errors (it's why Error relies on PartialEq).
My Code So Far
I have traits for Check and Error:
trait Check {
type Error;
fn check_number(&self, number: i32) -> Option<Self::Error>;
}
trait Error: std::fmt::Debug + PartialEq {
fn description(&self) -> String;
}
And two example checks, with their error structs. In this example, I want to show errors if a number is negative or even:
#[derive(PartialEq, Debug)]
struct EvenError {
number: i32,
}
struct EvenCheck;
impl Check for EvenCheck {
type Error = EvenError;
fn check_number(&self, number: i32) -> Option<EvenError> {
if number < 0 {
Some(EvenError { number: number })
} else {
None
}
}
}
impl Error for EvenError {
fn description(&self) -> String {
format!("{} is even", self.number)
}
}
#[derive(PartialEq, Debug)]
struct NegativeError {
number: i32,
}
struct NegativeCheck;
impl Check for NegativeCheck {
type Error = NegativeError;
fn check_number(&self, number: i32) -> Option<NegativeError> {
if number < 0 {
Some(NegativeError { number: number })
} else {
None
}
}
}
impl Error for NegativeError {
fn description(&self) -> String {
format!("{} is negative", self.number)
}
}
I know that in this example, the two structs look identical, but in my code, there are many different structs, so I can't merge them. Lastly, an example main function, to illustrate the kind of thing I want to do:
fn main() {
let numbers = vec![1, -4, 64, -25];
let checks = vec![
Box::new(EvenCheck) as Box<Check<Error = Error>>,
Box::new(NegativeCheck) as Box<Check<Error = Error>>,
]; // What should I put for this Vec's type?
for number in numbers {
for check in checks {
if let Some(error) = check.check_number(number) {
println!("{:?} - {}", error, error.description())
}
}
}
}
You can see the code in the Rust playground.
Solutions I've Tried
The closest thing I've come to a solution is to remove the associated types and have the checks return Option<Box<Error>>. However, I get this error instead:
error[E0038]: the trait `Error` cannot be made into an object
--> src/main.rs:4:55
|
4 | fn check_number(&self, number: i32) -> Option<Box<Error>>;
| ^^^^^ the trait `Error` cannot be made into an object
|
= note: the trait cannot use `Self` as a type parameter in the supertraits or where-clauses
because of the PartialEq in the Error trait. Rust has been great to me thus far, and I really hope I'm able to bend the type system into supporting something like this!
When you write an impl Check and specialize your type Error with a concrete type, you are ending up with different types.
In other words, Check<Error = NegativeError> and Check<Error = EvenError> are statically different types. Although you might expect Check<Error> to describe both, note that in Rust NegativeError and EvenError are not sub-types of Error. They are guaranteed to implement all methods defined by the Error trait, but then calls to those methods will be statically dispatched to physically different functions that the compiler creates (each will have a version for NegativeError, one for EvenError).
Therefore, you can't put them in the same Vec, even boxed (as you discovered). It's not so much a matter of knowing how much space to allocate, it's that Vec requires its types to be homogeneous (you can't have a vec![1u8, 'a'] either, although a char is representable as a u8 in memory).
Rust's way to "erase" some of the type information and gain the dynamic dispatch part of subtyping is, as you discovered, trait objects.
If you want to give another try to the trait object approach, you might find it more appealing with a few tweaks...
You might find it much easier if you used the Error trait in std::error instead of your own version of it.
You may need to impl Display to create a description with a dynamically built String, like so:
impl fmt::Display for EvenError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{} is even", self.number)
}
}
impl Error for EvenError {
fn description(&self) -> &str { "even error" }
}
Now you can drop the associated type and have Check return a trait object:
trait Check {
fn check_number(&self, number: i32) -> Option<Box<Error>>;
}
your Vec now has an expressible type:
let mut checks: Vec<Box<Check>> = vec![
Box::new(EvenCheck) ,
Box::new(NegativeCheck) ,
];
The best part of using std::error::Error...
is that now you don't need to use PartialEq to understand what error was thrown. Error has various types of downcasts and type checks if you do need to retrieve the concrete Error type out of your trait object.
for number in numbers {
for check in &mut checks {
if let Some(error) = check.check_number(number) {
println!("{}", error);
if let Some(s_err)= error.downcast_ref::<EvenError>() {
println!("custom logic for EvenErr: {} - {}", s_err.number, s_err)
}
}
}
}
full example on the playground
I eventually found a way to do it that I'm happy with. Instead of having a vector of Box<Check<???>> objects, have a vector of closures that all have the same type, abstracting away the very functions that get called:
fn main() {
type Probe = Box<Fn(i32) -> Option<Box<Error>>>;
let numbers: Vec<i32> = vec![ 1, -4, 64, -25 ];
let checks = vec![
Box::new(|num| EvenCheck.check_number(num).map(|u| Box::new(u) as Box<Error>)) as Probe,
Box::new(|num| NegativeCheck.check_number(num).map(|u| Box::new(u) as Box<Error>)) as Probe,
];
for number in numbers {
for check in checks.iter() {
if let Some(error) = check(number) {
println!("{}", error.description());
}
}
}
}
Not only does this allow for a vector of Box<Error> objects to be returned, it allows the Check objects to provide their own Error associated type which doesn't need to implement PartialEq. The multiple ases look a little messy, but on the whole it's not that bad.
I'd suggest you some refactoring.
First, I'm pretty sure, that vectors should be homogeneous in Rust, so there is no way to supply elements of different types for them. Also you cannot downcast traits to reduce them to a common base trait (as I remember, there was a question about it on SO).
So I'd use algebraic type with explicit match for this task, like this:
enum Checker {
Even(EvenCheck),
Negative(NegativeCheck),
}
let checks = vec![
Checker::Even(EvenCheck),
Checker::Negative(NegativeCheck),
];
As for error handling, consider use FromError framework, so you will able to involve try! macro in your code and to convert error types from one to another.