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

Calling a function with a literal and a variable requires manually writing .borrow() #492

Open
msrd0 opened this issue Jun 9, 2021 · 8 comments

Comments

@msrd0
Copy link
Contributor

msrd0 commented Jun 9, 2021

Using the latest release, we could write code like this (untested, from memory):

{{ foo.bar(value) }}
{{ foo.bar(1) }}
#[derive(Template)]
struct MyTemplate {
    foo: Foo,
    value: u32
}

struct Foo;
impl Foo {
    fn bar(value: &u32) -> String { format!("{}", value) }
}

Using the code currently on the main branch, foo.bar(1) gets translated to self.foo.bar(1), whereas foo.bar(value) gets translated to self.foo.bar(&self.value). This makes it impossible to write such a function without introducing a trait that is implemented for both u32 and &u32, as the function now needs to accept both values and references.

The only other workaround I could find was to use {{ foo.bar(1.borrow()) }} in the template code, which requires an explicit use std::borrow::Borrow in the file that invokes the derive macro. Still less code than defining my own trait.

@vallentin
Copy link
Collaborator

vallentin commented Jun 9, 2021

(I was already writing a comment in #490 before you posted this one, so I'll just post my final comment on this issue instead)


This was an intentional change, and it was done in PR #423. It changed so literals (and anything ending in .clone()) were no longer borrowed implicitly for e.g. function calls. Minor breakage was expected.

The issue was that it was impossible to call functions that didn't expect a reference, e.g. you wouldn't be able to call .iter().skip(5) or .iter().skip(amount.clone()), because skip() expects a usize and not a &usize.

The compromise was to change so literals were no longer implicitly borrowed for e.g. function calls. Because (as you already figured out) it's possible to call .borrow() to get e.g. a usize to a &usize, if you really need a &usize. However doing the opposite was/is not possible, as even if you had a fn deref(i: &usize) ->usize then it would just borrow it implicitly again. As in if you did the equivalent in Askama as vec.iter().skip(deref(5)), then it would generate vec.iter().skip(&deref(&5)) which could not be valid.

In short, if you really want a referenced literal, then .borrow() is always possible. However, literals not being implicitly referenced now, makes it possible to call all the functions you otherwise couldn't at all.


Yes it's annoying having to do .borrow(), however char is a primitive type that is also Copy. So no need to have (in your case) VirtualFont::glyph take a &char instead of a char :)

@msrd0
Copy link
Contributor Author

msrd0 commented Jun 9, 2021

I know, that function always took a &char because of this. Maybe it'd help if you allowed the reference operator to be used with literals, so that I can write font.glyph(&'a')? I know that you previously said that you don't want to introduce it, but that seems like the best solution I can think of. For all other use cases, I definitely like the change that literals can be used as literals and not only as references.

Another problem is that I usually only find the exact problem and the workaround by looking at the generated code and possibly compiling that to get the exact error location. Error messages like this one, for longer templates, don't really tell you much. Unfortunately, I have no idea how this could be improved.

error[E0308]: mismatched types
  --> codegen/src/main.rs:41:10
   |
41 | #[derive(Template)]
   |          ^^^^^^^^ expected `char`, found `&char`
   |
   = note: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)

@vallentin
Copy link
Collaborator

Just to be clear, the "you" you're referring to in this case is @djc. I don't think I've voiced my opinion on it. But I can definitely see the pros and cons of both view points :)

One thing though, if borrowing was added, then technically your example in the issue would still fail, as you'd still need to do {{ foo.bar(&1) }}

Personally, I'm open to and probably fine with any solution, as long as it doesn't e.g. minimize the amount of function calls it would be possible to make :)


Error messages like this one, for longer templates, don't really tell you much.

This is something we're well aware about. It personally bugs me a lot as well, as it sometimes confuses me a lot too. I've been looking into feasible ways to improve this, especially in relation to how all of it plays together with nom. But it's taking time :)

Related issue: #310

@msrd0
Copy link
Contributor Author

msrd0 commented Jun 9, 2021

Just to be clear, the "you" you're referring to in this case is @djc.

Sorry, my bad.

especially in relation to how all of it plays together with nom

I don't think the error message I posted above can be improved by nom at all. Neither nom nor askama's derive macro have enough information to know the parameters of the function I'm calling. Unless I understood something completely incorrectly, this error message has to always come from the rust compiler, and the most askama can do is hint the compiler to the "real" source of the error.

That being said, improving the error messages for invalid template syntax is of course also important :)

@vallentin
Copy link
Collaborator

Ahh, yeah, you're right. For some reason I was confusing the short error with parser errors :)

@djc
Copy link
Owner

djc commented Jun 21, 2021

I'm okay with the way passing a literal here doesn't work any more. It's definitely not enough to make me reconsider my stance on allowing & in templates.

@msrd0
Copy link
Contributor Author

msrd0 commented Jun 23, 2021

I'm not sure, how does #500 improve the situation here?

@djc djc reopened this Jun 24, 2021
@djc
Copy link
Owner

djc commented Jun 24, 2021

It doesn't, we referenced the wrong issue there (since corrected).

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

No branches or pull requests

3 participants