-
-
Notifications
You must be signed in to change notification settings - Fork 209
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
Use a separate trait for object safety #579
Conversation
Maybe lose the pub trait DynTemplate {
/// Helper method which allocates a new `String` and renders into it
fn dyn_render(&self) -> Result<String>;
/// Renders the template to the given `writer` buffer
fn dyn_render_into(&self, writer: &mut dyn std::fmt::Write) -> Result<()>;
/// Helper function to inspect the template's extension
fn extension(&self) -> Option<&'static str>;
/// Provides an conservative estimate of the expanded length of the rendered template
fn size_hint(&self) -> usize;
}
impl<T: Template> DynTemplate for T {
fn dyn_render(&self) -> Result<String> {
<Self as Template>::render(self)
}
fn dyn_render_into(&self, writer: &mut dyn std::fmt::Write) -> Result<()> {
<Self as Template>::render_into(self, writer)
}
fn extension(&self) -> Option<&'static str> {
Self::EXTENSION
}
fn size_hint(&self) -> usize {
Self::SIZE_HINT
}
} Otherwise DynTemplate isn't object-safe because all super-traits need to be object-safe, or am I misunderstanding the requirement in https://doc.rust-lang.org/reference/items/traits.html#object-safety? |
Ohh, good call, thanks! I thought we had some tests for this... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change looks sensible to me. 👍
Your assumption is right, they weren't stable yet. But yes, it seems to be more idiomatic to use associated constants, a bunch of I'm still waiting for |
762bf5b
to
46f1be1
Compare
I don't know if this diff is too ugly, but it could prevent accidental shadowing of the identifier diff --git a/askama/src/lib.rs b/askama/src/lib.rs
index c116f55..185e8cc 100644
--- a/askama/src/lib.rs
+++ b/askama/src/lib.rs
@@ -84,7 +84,7 @@ pub trait Template {
}
/// Renders the template to the given `writer` buffer
- fn render_into<W: std::fmt::Write + ?Sized>(&self, writer: &mut W) -> Result<()>;
+ fn render_into(&self, writer: &mut (impl std::fmt::Write + ?Sized)) -> Result<()>;
/// The template's extension, if provided
const EXTENSION: Option<&'static str>;
@@ -179,9 +179,9 @@ mod tests {
fn dyn_template() {
struct Test;
impl Template for Test {
- fn render_into<W: std::fmt::Write + ?Sized>(
+ fn render_into(
&self,
- writer: &mut W,
+ writer: &mut (impl std::fmt::Write + ?Sized),
) -> askama_shared::Result<()> {
Ok(writer.write_str("test")?)
}
diff --git a/askama_shared/src/generator.rs b/askama_shared/src/generator.rs
index bf553fb..ad7e1fe 100644
--- a/askama_shared/src/generator.rs
+++ b/askama_shared/src/generator.rs
@@ -128,7 +128,7 @@ impl<'a, S: std::hash::BuildHasher> Generator<'a, S> {
) -> Result<(), CompileError> {
self.write_header(buf, "::askama::Template", None)?;
buf.writeln(
- "fn render_into<W: ::std::fmt::Write + ?Sized>(&self, writer: &mut W) -> \
+ "fn render_into(&self, writer: &mut (impl ::std::fmt::Write + ?Sized)) -> \
::askama::Result<()> {",
)?; |
A manual implementation of |
Thanks for all the feedback! I think this is ready to go, and then I'll likely do a release soon. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM except for the two last comments.
This is relatively major change to the main trait's API. For context, I always started from the concept of monomorphized traits, but later several contributors asked about object safety. At that point I made `Template` object-safe, and then even later added a `SizedTemplate` to make some things easier for people who don't need object safety. However, having object-safety in the primary trait is bad for performance (a substantial number of calls into the virtual `Write` trait is relatively slow), and I don't think those who don't need object safety should pay for the cost of having it. Additionally, I feel using associated consts for the extension and size hint is more idiomatic than having accessor methods. I don't know why I didn't use these from the start -- maybe associated consts didn't exist yet, or I didn't yet know how/when to use them. Askama is pretty old at this point...
I was waiting for @vallentin's approval, but I guess it's fine in this case. :) |
I didn't have any additional "deny comments", which is why I just added regular comments. So it was fine with me :) |
This is a relatively major change to the main trait's API. For context,
I always started from the concept of monomorphized traits, but later
several contributors asked about object safety. At that point I made
Template
object-safe, and then even later added aSizedTemplate
to make some things easier for people who don't need object safety.
However, having object-safety in the primary trait is bad for
performance (a substantial number of calls into the virtual
Write
trait is relatively slow), and I don't think those who don't need
object safety should pay for the cost of having it.
Additionally, I feel using associated consts for the extension and
size hint is more idiomatic than having accessor methods. I don't
know why I didn't use these from the start -- maybe associated
consts didn't exist yet, or I didn't yet know how/when to use them.
Askama is pretty old at this point...