-
Notifications
You must be signed in to change notification settings - Fork 45
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
Embedding the Wrapper
object in a struct
#178
Comments
Hi @MathieuDuponchelle, thanks for the feedback and the question. It seems the link is dead, I get a 404 Not Found when I visit it just now. You raise a great point that I hadn't considered before... I'm also not an expert in this :-) Since the If you don't like creating two or more After some experimentation, I got this to compile: use hyphenation::{Language, Load, Standard};
use std::cell::RefCell;
use textwrap::{HyphenSplitter, NoHyphenation, WordSplitter, Wrapper};
struct MyWordSplitter {
splitter: RefCell<Box<dyn WordSplitter>>,
}
impl MyWordSplitter {
fn new() -> MyWordSplitter {
MyWordSplitter {
splitter: RefCell::new(Box::new(NoHyphenation)),
}
}
}
impl WordSplitter for &MyWordSplitter {
fn split<'w>(&self, word: &'w str) -> Vec<(&'w str, &'w str, &'w str)> {
self.splitter.borrow().split(word)
}
}
fn main() {
let text = "Here is some well-behaved automatic wrapping.";
let my_word_splitter = MyWordSplitter::new();
let wrapper = Wrapper::with_splitter(20, &my_word_splitter);
println!("NoHyphenation: {:#?}", wrapper.wrap(text));
*my_word_splitter.splitter.borrow_mut() = Box::new(HyphenSplitter);
println!("HyphenSplitter: {:#?}", wrapper.wrap(text));
let dictionary = Standard::from_embedded(Language::EnglishUS).unwrap();
*my_word_splitter.splitter.borrow_mut() = Box::new(dictionary);
println!("EnglishUS: {:#?}", wrapper.wrap(text));
} The output of
|
Oh my bad, the branch was merged since then and it was set to auto-delete on merge, here's how I ended up doing this: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/blob/master/text/wrap/src/gsttextwrap.rs#L100 , I do like it more than my initial version already :) Your solution is I think the most optimal in terms of memory usage, only disadvantage I would see is that it sort of exposes implementation details (I ideally would like to know nothing about that split method :)), I'll think about which one I want to pick and let you know. Thanks a lot for spending time on that exercise, I'll also be curious about your ideas for a different API, if you care to put down some pseudo code I can comment on it. CCing @sdroege, I had discussed the problem with him and I think he may have suggestions :) |
Before, the Wrapper would be generic in the type of WordSplitter used. This meant that Wrapper<NoHyphenation> would be a different type than Wrapper<HyphenSplitter>. The result is that you need to fix the type of WordSplitter at compile time. This makes it hard to make interactive programs, something which was raised in #178. Making splitter a Box<dyn WordSplitter> makes the field less special. We can therefore simplify the API by removing the with_splitter constructor. The performance is unchanged by this, even when testing with the hyphenation feature enabled.
Before, the Wrapper would be generic in the type of WordSplitter used. This meant that Wrapper<NoHyphenation> would be a different type than Wrapper<HyphenSplitter>. The result is that you need to fix the type of WordSplitter at compile time. This makes it hard to make interactive programs, something which was raised in #178. Making splitter a Box<dyn WordSplitter> makes the field less special. We can therefore simplify the API by removing the with_splitter constructor. The performance is unchanged by this, even when testing with the hyphenation feature enabled.
Before, the Wrapper would be generic in the type of WordSplitter used. This meant that Wrapper<NoHyphenation> would be a different type than Wrapper<HyphenSplitter>. The result is that you need to fix the type of WordSplitter at compile time. This makes it hard to make interactive programs, something which was raised in #178. Making splitter a Box<dyn WordSplitter> makes the field less special. We can therefore simplify the API by removing the with_splitter constructor. The performance is unchanged by this, even when testing with the hyphenation feature enabled.
This commit presents a minimal solution for mgeisler#178 which is yet quite flexible (as it allows using Box & Rc & others), as well as being fairly minimal, mostly, it is just relaxing trait bounds, thus this is even backwards compatible. In summary, this committ just relaxes the trait bound on the type parameter of `Wrapper`, to allow unsized `WordSpiltter`. While it is practically impossible to construct a `Wrapper` with unsized `WordSpiltter`, once a sized one has been created, and wrapped in some pointer type (such as Box), it can be coerced into e.g. `Box<Wrapper<dyn WordSpiltter>>` replacing the type parameter by a trait object.
With the changes in #206, you should be able to drop the enum Wrapper {
H(textwrap::Wrapper<'static, Standard>),
N(textwrap::Wrapper<'static, textwrap::NoHyphenation>),
} code since the type of the Since #206, I've renamed wrapper.fill("some text") to
where In your code, I believe you can then simply put a I made a little demo program here to test this kind of code myself: https://github.com/mgeisler/textwrap/blob/master/examples/interactive.rs#L191 Can I get you to try out the new API and let me know if you find any weirdness in the new API? |
This works fine but in @MathieuDuponchelle's code it only works after also doing #254 |
hrm, sorry this slipped off my mind, thanks for testing @sdroege :) |
Thanks both for helping test the new API, much appreciated! I think this can be closed now since #254 is on its way to being merged. |
Hey, thanks for the crate :)
I'm writing a simple GStreamer wrapper around it, my end use case is to format "any" text so that it can be used as input to a CEA 608 closed captioning element, CEA 608 exposes a simple grid system to position characters, that grid is at most 32 columns wide.
The element receives buffers of data, with a timestamp and a duration, and each buffer must be formatted as it arrives and sent out on the spot. I don't want to recreate a wrapper for each buffer, and instead store it in a
State
structure. In addition, the interface I exposed on the element accepts an optional path to a dictionary, which can be changed at runtime, including setting it toNone
to not use hyphenation at all.The implementation I ended up with is https://gitlab.freedesktop.org/meh/gst-plugins-rs/-/blob/textwrap/text/wrap/src/gsttextwrap.rs#L101 , and I'll continue that way for now, but that's not ideal: I'd much rather store a
Wrapper<WordSplitter>
, with its default set toWrapper::with_splitter(columns, NoHyphenation)
. I can't do this however of course, because the size ofWordSplitter
is not known at compile-time, but I also can't use Box because there is no impl for it either. I would also rather not have to use'static
as the lifetime here.I'm not very well-versed in terms of designing rust APIs so I don't have a good suggestion, but I hope we can come up with something a bit more elegant for that use case :)
Again, thanks for the crate, the functionality is exactly what I was looking for!
The text was updated successfully, but these errors were encountered: