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

Introduce a trait for WrapAlgorithm #325

Closed
mgeisler opened this issue Apr 19, 2021 · 0 comments · Fixed by #357
Closed

Introduce a trait for WrapAlgorithm #325

mgeisler opened this issue Apr 19, 2021 · 0 comments · Fixed by #357
Assignees

Comments

@mgeisler
Copy link
Owner

The WrapAlgorithm enum should be turned into a trait. This will allow us to

  1. Avoid doing .collect::<Vec<_>>() in our procesing pipeline. We do this today since the different wrapping algorithms return different Iterator types.
  2. Make the wrapping algorithm extensible by clients of the library.
@mgeisler mgeisler changed the title Make trait for WrapAlgorithm Make trait for WrapAlgorithm Apr 19, 2021
mgeisler added a commit that referenced this issue May 1, 2021
In #219, the type parameter for `Options` was relaxed to `?Sized`,
which means that `Options<T>` can be a dynamically-sized type by using
`dyn WordSplitter` as the type parameter. This allows code to freely
assign both boxed and unboxed `WordSplitter`s to the same variable:

```rust
let mut dynamic_opt: Box<Options<dyn WordSplitter>>;
dynamic_opt = Box::new(Options::with_splitter(20, NoHyphenation));
dynamic_opt = Box::new(Options::with_splitter(20, Box::new(NoHyphenation)));
```

In both cases, dynamic dispatch would be used at runtime. This was
called “proper outer boxing” in #219 and #215.

By only boxing the word splitter (so-called “inner boxing”), the outer
layer of indirection can be removed:

```rust
let mut dynamic_opt: Options<Box<dyn WordSplitter>>;
dynamic_opt = Options::with_splitter(20, Box::new(NoHyphenation));
dynamic_opt = Options::with_splitter(20, Box::new(HyphenSplitter));
```

This also used dynamic dispatch at runtime.

Static dispatching was also possible by using a fixed type. Trying to
change the word splitter type is a compile time error:

```rust
let mut static_opt: Options<NoHyphenation>;
static_opt = Options::with_splitter(10, NoHyphenation);
static_opt = Options::with_splitter(20, HyphenSplitter);  // <- error!
```

In order to add a trait for the `WrapAlgorithm` enum (see #325), we’re
now removing the `?Sized` bound on the `WordSplitter` type parameter.
This makes the first block above a compile time error and you must now
choose upfront between boxed and unboxed word splitters. If you choose
a boxed word splitter (second example), then you get dynamic dispatch
and can freely change the word splitter at runtime. If you choose an
unboxed wordsplitter, you get static dispatch and cannot change the
word splitter later.

This change seems necessary since we will be adding more type
parameters to the `Options` struct: one for the wrapping algorithm
used and one for how we find words (splitting by space or by the full
Unicode line breaking algorithm).

Since both dynamic and static dispatch remains possible, this change
should not cause big problems for Textwrap clients.
mgeisler added a commit that referenced this issue May 1, 2021
In #219, the type parameter for `Options` was relaxed to `?Sized`,
which means that `Options<T>` can be a dynamically-sized type by using
`dyn WordSplitter` as the type parameter. This allows code to freely
assign both boxed and unboxed `WordSplitter`s to the same variable:

```rust
let mut dynamic_opt: Box<Options<dyn WordSplitter>>;
dynamic_opt = Box::new(Options::with_splitter(20, NoHyphenation));
dynamic_opt = Box::new(Options::with_splitter(20, Box::new(NoHyphenation)));
```

In both cases, dynamic dispatch would be used at runtime. This was
called “proper outer boxing” in #219 and #215.

By only boxing the word splitter (so-called “inner boxing”), the outer
layer of indirection can be removed:

```rust
let mut dynamic_opt: Options<Box<dyn WordSplitter>>;
dynamic_opt = Options::with_splitter(20, Box::new(NoHyphenation));
dynamic_opt = Options::with_splitter(20, Box::new(HyphenSplitter));
```

This also used dynamic dispatch at runtime.

Static dispatching was also possible by using a fixed type. Trying to
change the word splitter type is a compile time error:

```rust
let mut static_opt: Options<NoHyphenation>;
static_opt = Options::with_splitter(10, NoHyphenation);
static_opt = Options::with_splitter(20, HyphenSplitter);  // <- error!
```

In order to add a trait for the `WrapAlgorithm` enum (see #325), we’re
now removing the `?Sized` bound on the `WordSplitter` type parameter.
This makes the first block above a compile time error and you must now
choose upfront between boxed and unboxed word splitters. If you choose
a boxed word splitter (second example), then you get dynamic dispatch
and can freely change the word splitter at runtime. If you choose an
unboxed wordsplitter, you get static dispatch and cannot change the
word splitter later.

This change seems necessary since we will be adding more type
parameters to the `Options` struct: one for the wrapping algorithm
used and one for how we find words (splitting by space or by the full
Unicode line breaking algorithm).

Since both dynamic and static dispatch remains possible, this change
should not cause big problems for Textwrap clients.
mgeisler added a commit that referenced this issue May 1, 2021
In #219, the type parameter for `Options` was relaxed to `?Sized`,
which means that `Options<T>` can be a dynamically-sized type by using
`dyn WordSplitter` as the type parameter. This allows code to freely
assign both boxed and unboxed `WordSplitter`s to the same variable:

```rust
let mut dynamic_opt: Box<Options<dyn WordSplitter>>;
dynamic_opt = Box::new(Options::with_splitter(20, NoHyphenation));
dynamic_opt = Box::new(Options::with_splitter(20, Box::new(NoHyphenation)));
```

In both cases, dynamic dispatch would be used at runtime. This was
called “proper outer boxing” in #219 and #215.

By only boxing the word splitter (so-called “inner boxing”), the outer
layer of indirection can be removed:

```rust
let mut dynamic_opt: Options<Box<dyn WordSplitter>>;
dynamic_opt = Options::with_splitter(20, Box::new(NoHyphenation));
dynamic_opt = Options::with_splitter(20, Box::new(HyphenSplitter));
```

This also used dynamic dispatch at runtime.

Static dispatching was also possible by using a fixed type. Trying to
change the word splitter type is a compile time error:

```rust
let mut static_opt: Options<NoHyphenation>;
static_opt = Options::with_splitter(10, NoHyphenation);
static_opt = Options::with_splitter(20, HyphenSplitter);  // <- error!
```

In order to add a trait for the `WrapAlgorithm` enum (see #325), we’re
now removing the `?Sized` bound on the `WordSplitter` type parameter.
This makes the first block above a compile time error and you must now
choose upfront between boxed and unboxed word splitters. If you choose
a boxed word splitter (second example), then you get dynamic dispatch
and can freely change the word splitter at runtime. If you choose an
unboxed wordsplitter, you get static dispatch and cannot change the
word splitter later.

This change seems necessary since we will be adding more type
parameters to the `Options` struct: one for the wrapping algorithm
used and one for how we find words (splitting by space or by the full
Unicode line breaking algorithm).

Since both dynamic and static dispatch remains possible, this change
should not cause big problems for Textwrap clients.
mgeisler added a commit that referenced this issue May 1, 2021
In #219, the type parameter for `Options` was relaxed to `?Sized`,
which means that `Options<T>` can be a dynamically-sized type by using
`dyn WordSplitter` as the type parameter. This allows code to freely
assign both boxed and unboxed `WordSplitter`s to the same variable:

```rust
let mut dynamic_opt: Box<Options<dyn WordSplitter>>;
dynamic_opt = Box::new(Options::with_splitter(20, NoHyphenation));
dynamic_opt = Box::new(Options::with_splitter(20, Box::new(NoHyphenation)));
```

In both cases, dynamic dispatch would be used at runtime. This was
called “proper outer boxing” in #219 and #215.

By only boxing the word splitter (so-called “inner boxing”), the outer
layer of indirection can be removed:

```rust
let mut dynamic_opt: Options<Box<dyn WordSplitter>>;
dynamic_opt = Options::with_splitter(20, Box::new(NoHyphenation));
dynamic_opt = Options::with_splitter(20, Box::new(HyphenSplitter));
```

This also used dynamic dispatch at runtime.

Static dispatching was also possible by using a fixed type. Trying to
change the word splitter type is a compile time error:

```rust
let mut static_opt: Options<NoHyphenation>;
static_opt = Options::with_splitter(10, NoHyphenation);
static_opt = Options::with_splitter(20, HyphenSplitter);  // <- error!
```

In order to add a trait for the `WrapAlgorithm` enum (see #325), we’re
now removing the `?Sized` bound on the `WordSplitter` type parameter.
This makes the first block above a compile time error and you must now
choose upfront between boxed and unboxed word splitters. If you choose
a boxed word splitter (second example), then you get dynamic dispatch
and can freely change the word splitter at runtime. If you choose an
unboxed wordsplitter, you get static dispatch and cannot change the
word splitter later.

This change seems necessary since we will be adding more type
parameters to the `Options` struct: one for the wrapping algorithm
used and one for how we find words (splitting by space or by the full
Unicode line breaking algorithm).

Since both dynamic and static dispatch remains possible, this change
should not cause big problems for Textwrap clients.
mgeisler added a commit that referenced this issue May 1, 2021
In #219, the type parameter for `Options` was relaxed to `?Sized`,
which means that `Options<T>` can be a dynamically-sized type by using
`dyn WordSplitter` as the type parameter. This allows code to freely
assign both boxed and unboxed `WordSplitter`s to the same variable:

```rust
let mut dynamic_opt: Box<Options<dyn WordSplitter>>;
dynamic_opt = Box::new(Options::with_splitter(20, NoHyphenation));
dynamic_opt = Box::new(Options::with_splitter(20, Box::new(NoHyphenation)));
```

In both cases, dynamic dispatch would be used at runtime. This was
called “proper outer boxing” in #219 and #215.

By only boxing the word splitter (so-called “inner boxing”), the outer
layer of indirection can be removed:

```rust
let mut dynamic_opt: Options<Box<dyn WordSplitter>>;
dynamic_opt = Options::with_splitter(20, Box::new(NoHyphenation));
dynamic_opt = Options::with_splitter(20, Box::new(HyphenSplitter));
```

This also used dynamic dispatch at runtime.

Static dispatching was also possible by using a fixed type. Trying to
change the word splitter type is a compile time error:

```rust
let mut static_opt: Options<NoHyphenation>;
static_opt = Options::with_splitter(10, NoHyphenation);
static_opt = Options::with_splitter(20, HyphenSplitter);  // <- error!
```

In order to add a trait for the `WrapAlgorithm` enum (see #325), we’re
now removing the `?Sized` bound on the `WordSplitter` type parameter.
This makes the first block above a compile time error and you must now
choose upfront between boxed and unboxed word splitters. If you choose
a boxed word splitter (second example), then you get dynamic dispatch
and can freely change the word splitter at runtime. If you choose an
unboxed wordsplitter, you get static dispatch and cannot change the
word splitter later.

This change seems necessary since we will be adding more type
parameters to the `Options` struct: one for the wrapping algorithm
used and one for how we find words (splitting by space or by the full
Unicode line breaking algorithm).

Since both dynamic and static dispatch remains possible, this change
should not cause big problems for Textwrap clients.
mgeisler added a commit that referenced this issue May 1, 2021
In #219, the type parameter for `Options` was relaxed to `?Sized`,
which means that `Options<T>` can be a dynamically-sized type by using
`dyn WordSplitter` as the type parameter. This allows code to freely
assign both boxed and unboxed `WordSplitter`s to the same variable:

```rust
let mut dynamic_opt: Box<Options<dyn WordSplitter>>;
dynamic_opt = Box::new(Options::with_splitter(20, NoHyphenation));
dynamic_opt = Box::new(Options::with_splitter(20, Box::new(NoHyphenation)));
```

In both cases, dynamic dispatch would be used at runtime. This was
called “proper outer boxing” in #219 and #215.

By only boxing the word splitter (so-called “inner boxing”), the outer
layer of indirection can be removed:

```rust
let mut dynamic_opt: Options<Box<dyn WordSplitter>>;
dynamic_opt = Options::with_splitter(20, Box::new(NoHyphenation));
dynamic_opt = Options::with_splitter(20, Box::new(HyphenSplitter));
```

This also used dynamic dispatch at runtime.

Static dispatching was also possible by using a fixed type. Trying to
change the word splitter type is a compile time error:

```rust
let mut static_opt: Options<NoHyphenation>;
static_opt = Options::with_splitter(10, NoHyphenation);
static_opt = Options::with_splitter(20, HyphenSplitter);  // <- error!
```

In order to add a trait for the `WrapAlgorithm` enum (see #325), we’re
now removing the `?Sized` bound on the `WordSplitter` type parameter.
This makes the first block above a compile time error and you must now
choose upfront between boxed and unboxed word splitters. If you choose
a boxed word splitter (second example), then you get dynamic dispatch
and can freely change the word splitter at runtime. If you choose an
unboxed wordsplitter, you get static dispatch and cannot change the
word splitter later.

This change seems necessary since we will be adding more type
parameters to the `Options` struct: one for the wrapping algorithm
used and one for how we find words (splitting by space or by the full
Unicode line breaking algorithm).

Since both dynamic and static dispatch remains possible, this change
should not cause big problems for Textwrap clients.
mgeisler added a commit that referenced this issue May 1, 2021
In #219, the type parameter for `Options` was relaxed to `?Sized`,
which means that `Options<T>` can be a dynamically-sized type by using
`dyn WordSplitter` as the type parameter. This allows code to freely
assign both boxed and unboxed `WordSplitter`s to the same variable:

```rust
let mut dynamic_opt: Box<Options<dyn WordSplitter>>;
dynamic_opt = Box::new(Options::with_splitter(20, NoHyphenation));
dynamic_opt = Box::new(Options::with_splitter(20, Box::new(NoHyphenation)));
```

In both cases, dynamic dispatch would be used at runtime. This was
called “proper outer boxing” in #219 and #215.

By only boxing the word splitter (so-called “inner boxing”), the outer
layer of indirection can be removed:

```rust
let mut dynamic_opt: Options<Box<dyn WordSplitter>>;
dynamic_opt = Options::with_splitter(20, Box::new(NoHyphenation));
dynamic_opt = Options::with_splitter(20, Box::new(HyphenSplitter));
```

This also used dynamic dispatch at runtime.

Static dispatching was also possible by using a fixed type. Trying to
change the word splitter type is a compile time error:

```rust
let mut static_opt: Options<NoHyphenation>;
static_opt = Options::with_splitter(10, NoHyphenation);
static_opt = Options::with_splitter(20, HyphenSplitter);  // <- error!
```

In order to add a trait for the `WrapAlgorithm` enum (see #325), we’re
now removing the `?Sized` bound on the `WordSplitter` type parameter.
This makes the first block above a compile time error and you must now
choose upfront between boxed and unboxed word splitters. If you choose
a boxed word splitter (second example), then you get dynamic dispatch
and can freely change the word splitter at runtime. If you choose an
unboxed wordsplitter, you get static dispatch and cannot change the
word splitter later.

This change seems necessary since we will be adding more type
parameters to the `Options` struct: one for the wrapping algorithm
used and one for how we find words (splitting by space or by the full
Unicode line breaking algorithm).

Since both dynamic and static dispatch remains possible, this change
should not cause big problems for Textwrap clients.
mgeisler added a commit that referenced this issue May 1, 2021
In #219, the type parameter for `Options` was relaxed to `?Sized`,
which means that `Options<T>` can be a dynamically-sized type by using
`dyn WordSplitter` as the type parameter. This allows code to freely
assign both boxed and unboxed `WordSplitter`s to the same variable:

```rust
let mut dynamic_opt: Box<Options<dyn WordSplitter>>;
dynamic_opt = Box::new(Options::with_splitter(20, NoHyphenation));
dynamic_opt = Box::new(Options::with_splitter(20, Box::new(NoHyphenation)));
```

In both cases, dynamic dispatch would be used at runtime. This was
called “proper outer boxing” in #219 and #215.

By only boxing the word splitter (so-called “inner boxing”), the outer
layer of indirection can be removed:

```rust
let mut dynamic_opt: Options<Box<dyn WordSplitter>>;
dynamic_opt = Options::with_splitter(20, Box::new(NoHyphenation));
dynamic_opt = Options::with_splitter(20, Box::new(HyphenSplitter));
```

This also used dynamic dispatch at runtime.

Static dispatching was also possible by using a fixed type. Trying to
change the word splitter type is a compile time error:

```rust
let mut static_opt: Options<NoHyphenation>;
static_opt = Options::with_splitter(10, NoHyphenation);
static_opt = Options::with_splitter(20, HyphenSplitter);  // <- error!
```

In order to add a trait for the `WrapAlgorithm` enum (see #325), we’re
now removing the `?Sized` bound on the `WordSplitter` type parameter.
This makes the first block above a compile time error and you must now
choose upfront between boxed and unboxed word splitters. If you choose
a boxed word splitter (second example), then you get dynamic dispatch
and can freely change the word splitter at runtime. If you choose an
unboxed wordsplitter, you get static dispatch and cannot change the
word splitter later.

This change seems necessary since we will be adding more type
parameters to the `Options` struct: one for the wrapping algorithm
used and one for how we find words (splitting by space or by the full
Unicode line breaking algorithm).

Since both dynamic and static dispatch remains possible, this change
should not fundamentally change the expressive power of the library.
@mgeisler mgeisler self-assigned this May 8, 2021
@mgeisler mgeisler changed the title Make trait for WrapAlgorithm Introduce a trait for WrapAlgorithm May 16, 2021
mgeisler added a commit that referenced this issue May 16, 2021
I plan to introduce a trait for the wrap algorithms (#325) and this
aligns better with such a trait. The reason is that a trait method
cannot have generic parameters, such as a `F: Fn(usize) -> usize`
parameter.
mgeisler added a commit that referenced this issue May 16, 2021
I plan to introduce a trait for the wrap algorithms (#325) and this
aligns better with such a trait. The reason is that a trait method
cannot have generic parameters, such as a `F: Fn(usize) -> usize`
parameter.
mgeisler added a commit that referenced this issue May 16, 2021
This introduces a `textwrap::wrap_algorithms` module where the
`wrap_first_fit` and `wrap_optimal_fit` functions now live.

A new `WrapAlgorithm` trait replaces the old enum. This allows for
arbitrary wrapping algorithms to be plugged into the library. We
should also be able to change the return type from `Vec<&[Word]>` to
`Iterator<Item = Word>` and thus implement streaming wrapping for the
first-fit in the future.

Fixes #325.
mgeisler added a commit that referenced this issue May 16, 2021
This introduces a `textwrap::wrap_algorithms` module where the
`wrap_first_fit` and `wrap_optimal_fit` functions now live.

A new `WrapAlgorithm` trait replaces the old enum. This allows for
arbitrary wrapping algorithms to be plugged into the library. We
should also be able to change the return type from `Vec<&[Word]>` to
`Iterator<Item = Word>` and thus implement streaming wrapping for the
first-fit in the future.

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

Successfully merging a pull request may close this issue.

1 participant