-
-
Notifications
You must be signed in to change notification settings - Fork 90
Improve glib::Value usage by merging FromValue/FromValueOptional traits #48
Comments
This looks better, indeed. I prefer this approach. |
Implemented it but it doesn't work that well with type-inference anymore: // Worked but does not work anymore
let x: i32 = v.get();
// Still works
let x = v.get::<i32>(); The reason is, I guess, because there could be multiple impls with the same target type. |
So if someone can make the following compile, let me know :) Otherwise I think having to require type annotations always is a bit suboptimal. pub trait FromValue<'a> {
type Target;
unsafe fn from_value(_: &'a Value) -> Self::Target;
}
impl<'a> FromValue<'a> for i32 {
type Target = i32;
unsafe fn from_value(_: &'a Value) -> i32 { unimplemented!() }
}
impl<'a> FromValue<'a> for String {
type Target = Option<String>;
unsafe fn from_value(_: &'a Value) -> Option<String> { unimplemented!() }
}
struct Value;
impl Value {
pub fn get<'a, T: FromValue<'a>>(&'a self) -> Result<T::Target, ()> { unimplemented!() }
}
fn main() {
let x: i32 = Value.get().unwrap();
let x: Option<String> = Value.get().unwrap();
let x = Value.get::<i32>().unwrap();
let x = Value.get::<String>().unwrap();
} |
Sorry about my previous comment, it was so damn stupid I deleted it ;( pub trait FromValue<'a> {
type Source;
unsafe fn from_value(_: &'a Value) -> Self;
}
impl<'a> FromValue<'a> for i32 {
type Source = i32;
unsafe fn from_value(_: &'a Value) -> i32 { unimplemented!() }
}
impl<'a> FromValue<'a> for Option<String> {
type Source = String;
unsafe fn from_value(_: &'a Value) -> Option<String> { unimplemented!() }
}
pub struct Value;
impl Value {
fn get<'a, T: FromValue<'a>>(&'a self) -> Result<T, ()> { unimplemented!() }
}
fn main() {
let x: i32 = Value.get().unwrap();
let x: Option<String> = Value.get().unwrap();
// doesn't allow for this sadly:
let x = Value.get::<i32>().unwrap();
let x = Value.get::<Option<String>>().unwrap();
} Edit: the |
I have no idea how to fix it, even if remove unneeded |
Yes, this isn't possible. @sdroege why do you want the generic argument and the return type to be disconnected like that? It could be done by having two traits, but I don't see the point in let x = Value.get::<i32>().unwrap(); // x is an i32
let x = Value.get::<String>().unwrap(); // x is an Option<String> |
I think the point is to specify the type of the value we want to obtain, but more importantly the right let x: Option<String> = Value.get().unwrap();
assert_eq!(x), Value.get::<String>().unwrap()); is a quite disturbing behavior and probably a counter-intuitive interface. |
Yeah I'm now also leaning towards specifying the type one would actually get. So The main issue here is then that the compiler error users would get for
Not sure why it doesn't even give suggestions for the impls here. |
For reference, it doesn't work out exactly as I expected (need an extra pub trait FromValue<'a> {
type Target: FromValueTarget<'a>;
unsafe fn from_value(_: &'a Value) -> Self::Target;
}
pub trait FromValueTarget<'a> {
type Source: FromValue<'a>;
}
impl<'a> FromValue<'a> for i32 {
type Target = i32;
unsafe fn from_value(_: &'a Value) -> i32 {
unimplemented!()
}
}
impl<'a> FromValueTarget<'a> for i32 {
type Source = i32;
}
impl<'a> FromValue<'a> for String {
type Target = Option<String>;
unsafe fn from_value(_: &'a Value) -> Option<String> {
unimplemented!()
}
}
impl<'a> FromValueTarget<'a> for Option<String> {
type Source = String;
}
pub struct Value;
impl Value {
pub fn get<'a, T, U>(&'a self) -> Result<U, ()>
where
T: FromValue<'a, Target = U>,
U: FromValueTarget<'a, Source = T>,
{
Ok(unsafe { T::from_value(self) })
}
}
fn main() {
let x: i32 = Value.get().unwrap();
let x: Option<String> = Value.get().unwrap();
let x = Value.get::<i32, _>().unwrap();
let x = Value.get::<String, _>().unwrap();
} |
Yeah, I was thinking about that too. rustc has its own attribute for that that can be applied to traits to give a custom error message, but of course that's perma-unstable: #![feature(rustc_attrs)]
#[rustc_on_unimplemented = "helpful error message"]
pub trait FromValue<'a> {
unsafe fn from_value(_: &'a Value) -> Self;
}
impl<'a> FromValue<'a> for i32 {
unsafe fn from_value(_: &'a Value) -> i32 { unimplemented!() }
}
impl<'a> FromValue<'a> for Option<String> {
unsafe fn from_value(_: &'a Value) -> Option<String> { unimplemented!() }
}
struct Value;
impl Value {
pub fn get<'a, T: FromValue<'a>>(&'a self) -> Result<T, ()> { unimplemented!() }
}
fn main() {
let x: i32 = Value.get().unwrap();
let x: Option<String> = Value.get().unwrap();
let x = Value.get::<i32>().unwrap();
let x = Value.get::<String>().unwrap();
} |
I was actually meaning to write an RFC for something like #[on_unimplemented = "error msg"]
impl !Trait for Type {} at one point, but never got around to it. |
Negative impls ? Isn't there aleady some work towards it in chalk ? Edit: sorry I didn't realized you were talking about |
Yes, negative impls for non-marker traits are an interest separately from an |
So I guess for now our options are either 1) two traits and another type parameter for let x: i32 = Value.get().unwrap();
let x: Option<String> = Value.get().unwrap();
let x = Value.get::<i32, _>().unwrap();
let x = Value.get::<String, _>().unwrap(); or 2) one simple trait and using the target type in both all cases (I wonder if it would make sense to also implement on let x: i32 = Value.get().unwrap();
let x: Option<String> = Value.get().unwrap();
let x = Value.get::<i32>().unwrap();
let x = Value.get::<Option<String>>().unwrap(); or 3) the status quo with Opinions? Vote on this comment with 👍 for 1), 🎉 for 2), 🚀 for 2) plus the part in the parenthesis, ❤️ for 3) and 👎 if you hate it all :) @GuillaumeGomez, @EPashkin, @jplatte @nightmared, @fengalin, @gdesmott to add a few people here but everybody else please also vote. |
Note that for 2), this is not an option directly because... we can't implement the trait on e.g. |
Note that for the option 2 with parenthesis, I think if you cannot specify a compile time error (as discussed above), then maybe it shouldn't be done. I really like the "If it compiles it works" approach, especially in Rust, as the type system is great for that ! IMHO Deferring preventable errors to runtime when they can be prevented at compile time is a trade-off that must be considered seriously. |
I agree with @nightmared on this one. So at least not option 2. |
@GuillaumeGomez I wouldn't say the option 2 itself is the issue, but rather the ability to report errors (although I'm not quite satisfied with option 2 either). More specifically: the tradeoff between runtime and compile time error. |
You're both misunderstanding option 2 with parenthesis then. There would be no runtime errors, let me explain :) Also type inference would work correctly with 2 in all regards. // This would work as long as the string is not None, otherwise it returns `Err(glib::value::GetError::UnexpectedNone)` or similar
let x: Result<String, GetError> = v.get::<String>();
// This would always work unless the type is just wrong and it e.g. stores an integer
let x: Result<Option<String>, GetError> = v.get::<Option<String>>(); The user would specify the type they want in the end. Do they want an optional string or do they assume that this string is never Addition: Also users assuming the string can never be |
With the full type declaration, it makes more sense. Then 🚀 for me. (option 2 with parenthesis) |
All right, thanks for clearing that up, I definitely misunderstood what you meant ;) |
Ok so I guess we have a winner. I'll look into implementing that. Main problem is the interactions with |
gtk-rs/glib#709 was the first part of this. With that out of the way, the traits can be cleaned up now. |
The main change is that it for non-nullable types `Value::get()` will never ever return `None`, replacing the previous confusing `Value::get_some()`. In addition it is now possible to `Value::get()` a nullable type directly without `Option` wrapping. If the value was actually `None` this would return an `Err`. There is also a new generic `ValueType` trait that is implemented by all types that can be used inside a `Value` and that allows storing/retrieving types of this value into/from a `Value`. Fixes gtk-rs#48
The main change is that it for non-nullable types `Value::get()` will never ever return `None`, replacing the previous confusing `Value::get_some()`. In addition it is now possible to `Value::get()` a nullable type directly without `Option` wrapping. If the value was actually `None` this would return an `Err`. There is also a new generic `ValueType` trait that is implemented by all types that can be used inside a `Value` and that allows storing/retrieving types of this value into/from a `Value`. Fixes gtk-rs#48
The main change is that it for non-nullable types `Value::get()` will never ever return `None`, replacing the previous confusing `Value::get_some()`. In addition it is now possible to `Value::get()` a nullable type directly without `Option` wrapping. If the value was actually `None` this would return an `Err`. There is also a new generic `ValueType` trait that is implemented by all types that can be used inside a `Value` and that allows storing/retrieving types of this value into/from a `Value`. Fixes gtk-rs#48
The main change is that it for non-nullable types `Value::get()` will never ever return `None`, replacing the previous confusing `Value::get_some()`. In addition it is now possible to `Value::get()` a nullable type directly without `Option` wrapping. If the value was actually `None` this would return an `Err`. There is also a new generic `ValueType` trait that is implemented by all types that can be used inside a `Value` and that allows storing/retrieving types of this value into/from a `Value`. Fixes gtk-rs#48
The main change is that it for non-nullable types `Value::get()` will never ever return `None`, replacing the previous confusing `Value::get_some()`. In addition it is now possible to `Value::get()` a nullable type directly without `Option` wrapping. If the value was actually `None` this would return an `Err`. There is also a new generic `ValueType` trait that is implemented by all types that can be used inside a `Value` and that allows storing/retrieving types of this value into/from a `Value`. Fixes gtk-rs#48
The main change is that it for non-nullable types `Value::get()` will never ever return `None`, replacing the previous confusing `Value::get_some()`. In addition it is now possible to `Value::get()` a nullable type directly without `Option` wrapping. If the value was actually `None` this would return an `Err`. There is also a new generic `ValueType` trait that is implemented by all types that can be used inside a `Value` and that allows storing/retrieving types of this value into/from a `Value`. Fixes gtk-rs#48
The main change is that it for non-nullable types `Value::get()` will never ever return `None`, replacing the previous confusing `Value::get_some()`. In addition it is now possible to `Value::get()` a nullable type directly without `Option` wrapping. If the value was actually `None` this would return an `Err`. There is also a new generic `ValueType` trait that is implemented by all types that can be used inside a `Value` and that allows storing/retrieving types of this value into/from a `Value`. Fixes gtk-rs#48
Currently we have
This has the effect that we have
while
v
can never ever beNone
here. There'sget_some()
which only exists forFromValue
types.I would suggest to instead have the following instead of the two traits
This would allow this confusion to disappear,
get::<i32>()
would returni32
andget::<String>()
would returnOption<String>
.SetValue
andSetValueOptional
would stay the same as there this problem doesn't really exist.@GuillaumeGomez @EPashkin Opinions?
The text was updated successfully, but these errors were encountered: