Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Specialize widget::text helper #2363

Merged
merged 5 commits into from
Apr 2, 2024
Merged

Specialize widget::text helper #2363

merged 5 commits into from
Apr 2, 2024

Conversation

hecrj
Copy link
Member

@hecrj hecrj commented Apr 1, 2024

This PR specializes the arguments of widget::text by manually implementing a new text::IntoFragment trait for the primitive types. No more extra allocations when calling text("Hello!") or text(some_string)! 🎉

The downside is that the text helper cannot be used with all the implementors of ToString anymore. However, it is always possible to implement the text::IntoFragment trait for local types.

Furthermore, a new widget::value helper has been introduced with the same functionality as the old text helper.

cc @alex-ds13

@hecrj hecrj added this to the 0.13 milestone Apr 1, 2024
@alex-ds13
Copy link
Contributor

Thank you! This is pretty good. I use a lot of Arc<str> on my apps which allow me to have one allocation with multiple references and I was avoiding using text() helper precisely because it would create a new allocation when it wasn't needed.

manually implementing a new text::IntoFragment trait for the primitive types

Did you leave out usize and isize on purpose or is it just missing?

In case of custom local types is there really any need to implement IntoFragment? Can't you simply call .to_string() on it? Won't IntoFragment have the same allocations as .to_string in those cases?

Isn't this:

struct MyStruct;
impl ToString for MyStruct {
     fn to_string(&self) -> String {
         "MyStruct".into()
     }
}
//...
App {
    my_struct: MyStruct
}

impl App {
  fn view(&self) -> Element<Message> {
      text(self.my_struct.to_string())
      .into()
  }
}

Pretty much the same thing as this:

struct MyStruct;
impl ToString for MyStruct {
     fn to_string(&self) -> String {
         "MyStruct".into()
     }
}
impl<'a> IntoFragment<'a> for MyStruct {
     fn into_fragment(self) -> Fragment<'a> {
         Fragment::Owned(self.to_string())
     }  
}
impl<'a> IntoFragment<'a> for &MyStruct {
     fn into_fragment(self) -> Fragment<'a> {
         Fragment::Owned(self.to_string())
     }  
}
//...
App {
    my_struct: MyStruct
}

impl App {
  fn view(&self) -> Element<Message> {
      text(&self.my_struct)
      .into()
  }
}

Both cases will allocate a new String won't they?

This means that for custom local or remote types that implement ToString you should simply call .to_string() on them.

@hecrj
Copy link
Member Author

hecrj commented Apr 2, 2024

@alex-ds13 Say MyStruct has a String, then you can:

struct MyStruct {
    some_text: String,
}

impl<'a> IntoFragment<'a> for &'a MyStruct {
    fn into_fragment(self) -> Content<'a> {
        Cow::Borrowed(&self.some_text)
    }
}

let my_struct = MyStruct { some_text: String::from("Some text!") };

text(&my_struct)

It saves you from typing text(&my_struct.some_text) everywhere and lets you hide implementation details better.

@hecrj
Copy link
Member Author

hecrj commented Apr 2, 2024

Also, ToString is never implemented directly—but instead it's implemented through Display, which invokes the fmt machinery. IntoFragment lets you skip that if you want.

@hecrj hecrj enabled auto-merge April 2, 2024 07:24
@hecrj
Copy link
Member Author

hecrj commented Apr 2, 2024

@alex-ds13 My last commit showcases a real use case of the pattern I talked about in the websocket example!

@alex-ds13
Copy link
Contributor

That's true. Overall I think this is a good change and if anyone needs to use a remote type that they can't implement IntoFragment they can always fallback to the value helper function. Or if they need to access an internal value like your example they can always wrap that type on a local type and implement IntoFragment for that.

@alex-ds13
Copy link
Contributor

Was there any technical reason for you to alias Cow to Fragment or was it just so it would fit better with the trait name?

@hecrj hecrj merged commit 6c75836 into master Apr 2, 2024
24 checks passed
@hecrj hecrj deleted the specialize-text-helper branch April 2, 2024 07:42
@hecrj
Copy link
Member Author

hecrj commented Apr 2, 2024

@alex-ds13 This is so you don't need yet an additional import (use std::borrow::Cow) to implement IntoFragment if you are already importing iced::widget::text.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants