rfcs
rfcs copied to clipboard
RFC: Dedented String Literals
Proposal to add dedented string literals to Rust of the form: d"string".
With the following:
let sql = d"
create table student(
id int primary key,
name text
)
";
Being equivalent to:
let sql = "\
create table student(
id int primary key,
name text
)";
Whitespace Sensitivity
I find it very unfortunate that your proposal relies on whitespace sensitivity, and I am afraid it's going to be an uphill challenge due to that.
Apart from the challenges in figuring out the exact indent to deindent, there are also visual challenges when wanting leading whitespace on some of the lines, for example when embedding code. And if they're hard challenges for regular users, I dread to think how challenging they'll be for visually impaired users.
Counter Proposal
Switch to something closer to Zig's multiline strings: a prefix on each line:
fn main() {
println!(
u"create table student(
u" id int primary key,
u" name text,
u")
u"
));
}
Specifically speaking:
u"signals the beginning of a string fragment, which runs until the end of the line.- When a string fragment is followed by another string fragment, it contains the (CR) LF character(s) which end the line, whereas when a string fragment is followed by another token, it does NOT contain any trailing (CR) LF character(s) ending its line.
This solution is much easier to use because it offers a clear separator between meaningless indentation (before u") and meaningful indentation (after u"). On top of the visual clarity is also makes copy/pasting easier: I can pick a line from another multi-line string and paste it in the middle, and its indentation is preserved by default, rather than being smashed by the "helpful" text editor.
cc @Diggsey (for RFC #3450) @pitaj (for RFC #3475) @workingjubilee @the8472 @tgross35
- https://github.com/rust-lang/rfcs/pull/3450
- https://github.com/rust-lang/rfcs/pull/3475
Replying to https://github.com/rust-lang/rfcs/pull/3830#issuecomment-2949935003
Counter Proposal
Switch to something closer to Zig's multiline strings: a prefix on each line:
fn main() { println!( u"create table student( u" id int primary key, u" name text, u") u" )); }
Agreed on the benefits you mentioned (clear separation of the string value VS code outside, better copy paste behavior) this also enables to interleave code comments between string fragments to explain parts of the string wihtout appearing in the output. For longer and more complex strings this may be useful. I also really like how the behavior of the final newline does not require special-casing.
Re: https://github.com/rust-lang/rfcs/pull/3830#issuecomment-2949935003 @matthieu-m
I strongly disagree on almost every point:
I find it very unfortunate that your proposal relies on whitespace sensitivity, and I am afraid it's going to be an uphill challenge due to that.
I don't understand this objection at all. Every string literal in Rust is whitespace sensitive. What makes this proposal special?
This solution is much easier to use because it offers a clear separator between meaningless indentation (before u") and meaningful indentation (after u").
The two character prefix throws the indentation level of the inner code out of line with the indentation levels of the rest of the code. Even worse if using tabs.
If for some reason the literal is so long that it's difficult to see where the left margin begins, editors can easily show a vertical line separating the two sides since this is functionality that already exists for tab levels. In general though... this isn't exactly the biggest hurdle to reading code, and it pre-supposes that the distinction actually matters, which in most cases it won't.
On top of the visual clarity is also makes copy/pasting easier: I can pick a line from another multi-line string and paste it in the middle, and its indentation is preserved by default, rather than being smashed by the "helpful" text editor.
This is where you lose me completely. With a prefix on every line it's impossible to copy/paste code into or out of the string literal which is crucial functionality. Furthermore, editor auto-formatting is typically triggered only be natural typing, not when pasting code, so it seems like a non-issue to begin with?
This alone makes this alternative a non-starter for me.
Re: #3830 (comment) @matthieu-m
I strongly disagree on almost every point:
I find it very unfortunate that your proposal relies on whitespace sensitivity, and I am afraid it's going to be an uphill challenge due to that.
I don't understand this objection at all. Every string literal in Rust is whitespace sensitive. What makes this proposal special?
No, they're not.
Or rather, string literals preserve whitespace that is part of the string, but are completely unaffected by whitespace outside the string.
The problem of this proposal, as is, is that there's no clear separation between the "outside" and "inside" whitespace, that is, if I give you:
let sql = d"
create table student(
id int primary key,
name text
Can you tell me what is, and what isn't part of the string?
In fact, even if I give you the full string:
impl X {
fn foo() {
let sql = d"
SELECT
COALESCE(acquisitions.month, investments.month) AS month,
acquisitions.companies_acquired,
investments.companies_rec_investment
FROM (
SELECT
acquired_month AS month,
COUNT(DISTINCT company_permalink) AS companies_acquired
FROM tutorial.crunchbase_acquisitions
GROUP BY 1
) acquisitions
FULL JOIN (
SELECT
funded_month AS month,
COUNT(DISTINCT company_permalink) AS companies_rec_investment
FROM tutorial.crunchbase_investments
GROUP BY 1
)investments
ON acquisitions.month = investments.month
ORDER BY 1 DESC
";
}
}
Can you easily spot if every line is correctly indented? Or if there's spurious whitespace?
Not so easy, is it.
This solution is much easier to use because it offers a clear separator between meaningless indentation (before u") and meaningful indentation (after u").
The two character prefix throws the indentation level of the inner code out of line with the indentation levels of the rest of the code. Even worse if using tabs.
So? For most text there's no indentation at all anyway.
SQL may be a bit of a special case here -- being embedded code itself -- but that could always be arranged by having u.." or whatever if it's really considered important that code line up.
If for some reason the literal is so long that it's difficult to see where the left margin begins, editors can easily show a vertical line separating the two sides since this is functionality that already exists for tab levels. In general though... this isn't exactly the biggest hurdle to reading code, and it pre-supposes that the distinction actually matters, which in most cases it won't.
Let's design a language to be readable outside of IDEs, please.
There's many places where the code is displayed outside an IDE, starting with this very website we're using here.
On top of the visual clarity is also makes copy/pasting easier: I can pick a line from another multi-line string and paste it in the middle, and its indentation is preserved by default, rather than being smashed by the "helpful" text editor.
This is where you lose me completely. With a prefix on every line it's impossible to copy/paste code into or out of the string literal which is crucial functionality. Furthermore, editor auto-formatting is typically triggered only be natural typing, not when pasting code, so it seems like a non-issue to begin with?
This alone makes this alternative a non-starter for me.
Let's take the previous complex SQL query as an example, and say that I want to copy the select from tutorial.crunchbase_investments from another query into this one. Those two queries may have different indentation levels, for example, maybe I'm copying from a top-level const, only indented 4 spaces, into this sql variable here, indented 12 spaces.
With this proposal, I have to manually indent the subquery because the IDE has no clue how much indentation I'm going for.
With my counter-proposal, however, it's immediately obvious to the IDE that the u" should line up, and therefore it can add the 8 spaces of indentation to line things up.
That is:
// With this proposal, I end up with:
impl X {
fn foo() {
let sql = d"
SELECT
COALESCE(acquisitions.month, investments.month) AS month,
acquisitions.companies_acquired,
investments.companies_rec_investment
FROM (
SELECT
acquired_month AS month,
COUNT(DISTINCT company_permalink) AS companies_acquired
FROM tutorial.crunchbase_acquisitions
GROUP BY 1
) acquisitions
FULL JOIN (
SELECT
funded_month AS month,
COUNT(DISTINCT company_permalink) AS companies_rec_investment
FROM tutorial.crunchbase_investments
GROUP BY 1
)investments
ON acquisitions.month = investments.month
ORDER BY 1 DESC
";
}
}
// With my counter proposal I end up with:
impl X {
fn foo() {
let sql =
u"SELECT
u" COALESCE(acquisitions.month, investments.month) AS month,
u" acquisitions.companies_acquired,
u" investments.companies_rec_investment
u" FROM (
u" SELECT
u" acquired_month AS month,
u" COUNT(DISTINCT company_permalink) AS companies_acquired
u" FROM tutorial.crunchbase_acquisitions
u" GROUP BY 1
u") acquisitions
u"FULL JOIN (
u" SELECT
u" funded_month AS month,
u" COUNT(DISTINCT company_permalink) AS companies_rec_investment
u" FROM tutorial.crunchbase_investments
u" GROUP BY 1
u")investments
u"ON acquisitions.month = investments.month
u"ORDER BY 1 DESC
u";
u"
}
}
And yes, to answer the question, the FROM was ill-indented by one-space since the beginning...
And it doesn't only affect IDEs, it also affects rustfmt.
My counter-proposal makes rustfmt work.
Counter Proposal
Switch to something closer to Zig's multiline strings: a prefix on each line:
fn main() { println!( u"create table student( u" id int primary key, u" name text, u") u" )); }
I like the general idea of this, but I have noticed one issue: what happens when you want to use mutiple string modifiers? Do you then have to repeat all of them on every line?
Another issue is that you cannot just paste code into it, although out of the two proposals, as long as your editor can edit multiple lines at once, adding the u" prefix after pasting the code and then running rustfmt sounds much easier than manually adjusting the whitespace of the pasted in code with the original proposal.
This is something I can bikeshed.
(Disclaimer, I type mostly Python.)
The string literal style I mostly miss -- indentation wise -- in rust is concatenated strings: ("abc " "def") == "abc def"
If you're going to suggest:
fn main() {
println!(
u"create table student(
u" id int primary key,
u" name text,
u")
u"
));
}
Then I'd rather just see:
fn main() {
println!(
"create table student(\n"
" id int primary key,\n"
" name text,\n"
")\n"
));
}
For full multiline things, there should not be a prefix u" in it. If there is, there is no way I can copy-paste things in and out of it. Unacceptable.
As for the dedent:
multiline = dedent("""
SELECT *
FROM abc;
""")
Can't you come up with a macro! that does dedent()? Maybe also one that replaces all [[:white:]]+ with a single blank.
Counter Proposal
Switch to something closer to Zig's multiline strings: a prefix on each line:
fn main() { println!( u"create table student( u" id int primary key, u" name text, u") u" )); }
I agree with a lot of your reasoning, but I am against any change that would introduce new uses of unpaired quotation marks into the language. Also, having to type a multi-character prefix on every line is going to be obnoxious, particularly if other modifiers are needed as well.
What if we mash up this with my suggestion to make the first non-whitespace character on the first line of the dedented string control the indentation level? I had
fn main() {
println!(d"
create table student (
id int primary key,
name text,
");
}
where the 'c' on the first line would control the amount of whitespace stripped, and then there was some stuff about putting a backslash earlier in the string if you wanted to preserve some of the whitespace. But we could equally well say that every line of a d"..." string must begin with a "prefix" which is some amount of whitespace followed by an arbitrary punctuation character (conventionally '|'), all of which is removed. All lines must have consistent prefixes. We keep the special treatment of \n right at the beginning and \n\h* right at the end (where \h means horizontal whitespace, as in Perl regexes).
/// "create table student (\n id int primary key,\n name text,\n)"
let ex1 = d"
|create table student (
| id int primary key,
| name text,
|)
";
/// " create table student (\n id int primary key,\n name text,\n )"
let ex2 = d"
| create table student (
| id int primary key,
| name text,
| )
";
This would also address the "but what if you want a trailing newline" issue cleaner than anything I saw upthread.
/// " drop table student"
let ex3 = d"
| drop table student";
/// " drop table student"
let ex4 = d"
| drop table student
";
/// " drop table student\n"
let ex4 = d"
| drop table student
|";
/// " drop table student\n"
let ex4 = d"
| drop table student
|
";
/// " drop table student\n\n"
let ex4 = d"
| drop table student
|
|";
So, chatgpt hooked me up:
extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, LitStr};
#[proc_macro]
pub fn dedent(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as LitStr);
let original = input.value();
// Split into lines and trim leading/trailing blank lines
let mut lines: Vec<&str> = original.lines().collect();
while lines.first().map_or(false, |l| l.trim().is_empty()) {
lines.remove(0);
}
while lines.last().map_or(false, |l| l.trim().is_empty()) {
lines.pop();
}
// Determine minimum indent (spaces only)
let min_indent = lines
.iter()
.filter(|line| !line.trim().is_empty())
.map(|line| line.chars().take_while(|c| *c == ' ').count())
.min()
.unwrap_or(0);
// Strip the indent
let dedented: String = lines
.iter()
.map(|line| {
if line.len() >= min_indent {
&line[min_indent..]
} else {
*line
}
})
.collect::<Vec<&str>>()
.join("\n");
TokenStream::from(quote! {
#dedented
})
}
Could be all bad code. I've never written a macro. But it appears to work at first glance:
use dedent_macro::dedent;
fn main() {
println!(dedent!(r#"
Hello,
you unforgiving
world!
--someuser
"#));
}
Output:
$ ./target/debug/dedent_test
Hello,
you unforgiving
world!
--someuser
LGTM.
Do we really need d". Should we maybe advocate for "abc " "def" concatenation instead?
The string literal style I mostly miss -- indentation wise -- in rust is concatenated strings:
("abc " "def")=="abc def"
I would be in favor of adding concatenated string notation of some sort to Rust. (We do have concat!, but that adds an extra layer of indentation and prevents use of {variable} in format strings.) However, I don't think they are a complete substitute for this proposal, because of what I like to call the "help string interword space problem."
def main():
ap = argparse.ArgumentParser()
ap.add_argument(
"-o", "--output",
help=(
"Write the new or updated label to this file."
" If omitted, defaults to <directory>/CONTENTS.YML."
" If that file already exists it will be replaced."
)
)
# ...
See the leading space inside the second and third of the strings? It's really easy to forget to do that, and really easy to miss it in code review, but then you have a missing interword space in your help text! And so I've seen people do this instead:
def main():
ap = argparse.ArgumentParser()
ap.add_argument(
"-o", "--output",
help=textwrap.dedent("""
Write the new or updated label to this file.
If omitted, defaults to <directory>/CONTENTS.YML.
If that file already exists it will be replaced.
""")
)
# ...
... which is (in somewhat different form) the feature proposed by this RFC ;-)
Technically you have two different strings there:
'Write the new or updated label to this file. If omitted, defaults to <directory>/CONTENTS.YML. If that file already exists it will be replaced.'
and
'\nWrite the new or updated label to this file.\nIf omitted, defaults to <directory>/CONTENTS.YML.\nIf that file already exists it will be replaced.\n'
The default argumentparser is "friendly" enough to strip and replace your LFs with spaces in the output. You don't even need the dedent in this case. And when you do, you'll need a strip().
But, I have seen a couple of cases of your help string interword space problem, so I'll give you that.
However, if this d" string is really tackling the help problem above, it would actually need to
re.sub(r'\s+', ' ', " Long miltiline\n help text.\n ".strip())
which I don't think you're proposing here.
@matthieu-m
Or rather, string literals preserve whitespace that is part of the string, but are completely unaffected by whitespace outside the string.
Not true:
https://doc.rust-lang.org/reference/expressions/literal-expr.html#r-expr.literal.continuation
There's already precedent in the language for ignoring some whitespace between the double quotes. IMO the distinction you're drawing here is completely arbitrary. The only change in this RFC is what whitespace gets ignored.
The problem of this proposal, as is, is that there's no clear separation between the "outside" and "inside" whitespace, that is, if I give you: ... Can you tell me what is, and what isn't part of the string?
Yes if you can't see the closing quote you can't tell how many levels of indentation will be ignored. I can still easily understand the contents of the string without that information: it's not necessary to "front-load" that - if it matters I can just look at the closing quote.
In fact, even if I give you the full string:
Can you easily spot if every line is correctly indented? Or if there's spurious whitespace?
Not so easy, is it.
Honestly that seems very clear to me?
So? For most text there's no indentation at all anyway
I think a lot of the drive from this feature comes from embedding code, which is quite likely to have indentation.
Let's design a language to be readable outside of IDEs, please.
Your proposal is impossible to write without an IDE, that's much worse than an IDE making it slightly clearer to read from what is already a pretty clear starting point.
Let's take the previous complex SQL query as an example, and say that I want to copy the select from tutorial.crunchbase_investments from another query into this one. Those two queries may have different indentation levels, for example, maybe I'm copying from a top-level const, only indented 4 spaces, into this sql variable here, indented 12 spaces.
With this proposal, I have to manually indent the subquery because the IDE has no clue how much indentation I'm going for.
You paste in the code and if it's not at the level you want, you select the whole thing and press [tab] or [shift+tab] to get it to the correct level - functionality that's present even the barest of text editor.
With my counter-proposal, however, it's immediately obvious to the IDE that the u" should line up, and therefore it can add the 8 spaces of indentation to line things up.
There is no u" in the code being copied, so the IDE (I thought you wanted code that worked without an IDE anyway...) has no idea how to line it up? Why are you assuming the code is being copied or pasted to/from another dedented string literal? That's not going to be the case normally.
And it doesn't only affect IDEs, it also affects rustfmt. My counter-proposal makes rustfmt work.
Rustfmt works just fine without your proposal - it preserves the value of the literal but is free to adjust the overall indentation level to suite the surrounding code.
Also, having to type a multi-character prefix on every line is going to be obnoxious, particularly if other modifiers are needed as well.
//! 🤭
I do find typing //! over and over in a long module doc comment obnoxious, as it happens. I kinda wish the style for those was /*! ... */ with the bulk of the text indented by four spaces but otherwise unmarked. (I dunno if that even works.)
(I find /// substantially less annoying, because it's three taps on a single key and doesn't involve Shift.)
Please, everyone try to leave an inline review for your comments. Feel free to comment on the RFC's .md file itself if you have no particular part of the RFC you are reacting to. This particular... class of proposals is prone to receiving a LOT of comments, and we'd be best off if each was its own thing and could be separately resolved.
@matthieu-m
Or rather, string literals preserve whitespace that is part of the string, but are completely unaffected by whitespace outside the string.
Not true:
https://doc.rust-lang.org/reference/expressions/literal-expr.html#r-expr.literal.continuation
There's already precedent in the language for ignoring some whitespace between the double quotes. IMO the distinction you're drawing here is completely arbitrary. The only change in this RFC is what whitespace gets ignored.
TIL!
In fact, even if I give you the full string: Can you easily spot if every line is correctly indented? Or if there's spurious whitespace? Not so easy, is it.
Honestly that seems very clear to me?
Well, you've got better eyes than I do, then, because I sense something is wrong, but I'm not quite sure whether it's aligned or not. And I doubt I'm the only one.
So? For most text there's no indentation at all anyway
I think a lot of the drive from this feature comes from embedding code, which is quite likely to have indentation.
That's not explicit in the motivation.
I personally would use it for a lot more than just code.
Let's design a language to be readable outside of IDEs, please.
Your proposal is impossible to write without an IDE, that's much worse than an IDE making it slightly clearer to read from what is already a pretty clear starting point.
Of course it's possible. Any text editor worth its salt has multi-caret editing. Even the Rust Playground has multi-caret editing.
Let's take the previous complex SQL query as an example, and say that I want to copy the select from tutorial.crunchbase_investments from another query into this one. Those two queries may have different indentation levels, for example, maybe I'm copying from a top-level const, only indented 4 spaces, into this sql variable here, indented 12 spaces. With this proposal, I have to manually indent the subquery because the IDE has no clue how much indentation I'm going for.
You paste in the code and if it's not at the level you want, you select the whole thing and press [tab] or [shift+tab] to get it to the correct level - functionality that's present even the barest of text editor.
That's a whole level of ergonomics down from it automatically working.
With my counter-proposal, however, it's immediately obvious to the IDE that the u" should line up, and therefore it can add the 8 spaces of indentation to line things up.
There is no u" in the code being copied, so the IDE [...] has no idea how to line it up? Why are you assuming the code is being copied or pasted to/from another dedented string literal? That's not going to be the case normally.
I am obviously referring to copying from code. An important usecase in refactoring, and in general working with code, is the ability to move code, or portions of code around, as I explained in the lead to the example I gave: copying a sub-clause from another SQL query.
Strings will be copied from outside code too, obviously, but I am afraid then I don't quite see how tooling can help.
(I thought you wanted code that worked without an IDE anyway...)
I want code to be readable without an IDE; a major usecase being reviewing code online (GitHub, GitLab, ...).
I've got nothing against IDEs, or other tooling, assisting to make editing smoother. I just see it as orthogonal: a different usecase.
And it doesn't only affect IDEs, it also affects rustfmt. My counter-proposal makes rustfmt work.
Rustfmt works just fine without your proposal - it preserves the value of the literal but is free to adjust the overall indentation level to suite the surrounding code.
No, it doesn't.
With this RFC's proposal, rustfmt must treat the entire string as a single blob. It can indent/dedent the blob, but that's all the level of assistance it can offer.
With my counter-proposal, rustfmt, and other tooling, can act at the level of string fragments, indenting/dedenting them separately from one another, and thus fixing any copy/paste indentation mismatch arising from moving fragments around. Such as copying a SQL sub-clause from one query to another.
I do find typing
//!over and over in a long module doc comment obnoxious, as it happens. I kinda wish the style for those was/*! ... */with the bulk of the text indented by four spaces but otherwise unmarked. (I dunno if that even works.)(I find
///substantially less annoying, because it's three taps on a single key and doesn't involve Shift.)
Two solutions there:
- Use an IDE which automatically inserts
//!when you type enter from a//!line. - Type the entire doc comment, then multi-caret edit the whole thing at once: you'll type
//!once and it'll just appear on all lines.
Of course it's possible. Any text editor worth its salt has multi-caret editing. Even the Rust Playground has multi-caret editing.
Firstly, no, many editors do not have multi-caret editing. The editor I'm typing in right now does not for example... Secondly, even with multi-caret editing you still have to actually put all those carets in place somehow which requires not just multi-caret but also box-selection functionality to do remotely efficiently.
I am obviously referring to copying from code.
But that's far from the only time copy/paste is used. I would say the majority of copy/paste would be eg. copying a query from my SQL client to my editor. Or copying code from the editor into the browser to include as part of a comment, or a miriad of other use-cases where having a bunch of extra characters at the start of every line catastrophically breaks the workflow, to the point I'd rather stick with Rust's existing multi-line strings.
With this RFC's proposal, rustfmt must treat the entire string as a single blob. It can indent/dedent the blob, but that's all the level of assistance it can offer.
Yeah that's all I would want it to do...
With my counter-proposal, rustfmt, and other tooling, can act at the level of string fragments
Saving zero to two presses of the [tab] key per refactor at the cost of completely breaking copy/paste is not a trade-off I would make.
First,
Dedented string literals must begin with a newline
- The opening line (the line containing the opening quote ") Must only contain a literal newline character after the
"token This newline is removed.
Really? Is it user-friendly?
let xxx = d"••
••••••••••••my new line
"
As for me it is quite OK, but with your rule this is an error! Let you change the rule to Dedented string literals must begin with a newline or whitespaces and a newline
Second,
Right now d"strings" is .... error-written dedented string. But do we really wish that?
So, maybe an additional 0th rule is needed
- If Dedented string does not contain a literal newline, it is parsed as a regular (non-dedented) string
With such rule d"strings" becomes a valid string.
Could you yelp me understand why someone would use d"" when the string won't be dedented?
The main reason I could see for having content on the first line is along with why the last line can be over-indented, to turn an error into a rustfmt clean up.
If it were allowed though, you'd then have to decide how it interacts with dedenting. Is it ignored for counting what the dedented whitespace is?
Also, will the fact the first newline cause confusion for people if they can optionally not have it?
change the rule to Dedented string literals must begin with a newline or whitespaces and a newline
I think that's a good idea.
- If Dedented string does not contain a literal newline, it is parsed as a regular (non-dedented) string
I do not think that's a good idea, since imo it would make it more confusing for no benefit. if you want a string literal to fit on one line, then don't use a dedented string. the compiler error message can say that.
@programmerjake
change the rule to Dedented string literals must begin with a newline or whitespaces and a newline
I think that's a good idea.
Thank you!
I do not think that's a good idea, since imo it would make it more confusing for no benefit. if you want a string literal to fit on one line, then don't use a dedented string. the compiler error message can say that.
Ok, I agree - minimum benefits
Let you change the rule to Dedented string literals must begin with a newline or whitespaces and a newline
~Another downside here is that if we ever want to add something like info strings in the future, it'll be much harder since we will now have to account for "what do we do for all dedented strings where the line of the opening quote has trailing whitespace"~
edit: this is not a problem. Hence I have added it to the RFC
@nik-rev Another downside here is that if we ever want to add something like info strings in the future, it'll be much harder
I really don't see how whitespaces only at quote line will complicate anything in future. Meta-data or some other info are not whitespaces only.
@matthieu-m While I normally have a great deal of appreciation for your takes on things...
- I think your alternative proposal is just plain ugly
- I still don't like literal lifetime syntax and think adding new un-paired quotes is such a mind-bogglingly bad idea that I didn't notice that was what you were proposing until I saw someone else calling it out. I was just subconsciously typo-fixing your example to a More Python-esque
u"..."\nu"..."\nu"..."syntax. - I think it's comparable to shunning Python's multi-line string support in favour of a bunch of
" ... " +lines... with the presence of the+being mandatory. - As others have pointed out, it's hostile to copying and pasting.
Were it to be implemented, my PR guidelines would require that regular strings and textwrap::dedent be used in its place in my projects. (Which is essentially the Python approach, but achieved through a third-party crate.)
@zackw I find it easy to not forget the help string interword space if I just make it a policy that ends of lines must have them (i.e. foo. ", not " Bar)
@epage
Could you yelp me understand why someone would use
d""when the string won't be dedented?
Same reason Rust allows various "weird" or "ugly" things that macros might emit... to minimize the number of special cases that the programmer or macro author needs to keep in mind.
In this case, I could see myself flipping back and forth between the two forms during RAD/prototyping and find it frustrating that I have to add or remove a d each time.
Think of it as akin to allowing a trailing , in a collection literal so people don't have to keep adding and removing , as they reorganize/add/remove entries. (I'm looking at you, JSON.)
I really don't see how whitespaces only at quote line will complicate anything in future. Meta-data or some other info are not whitespaces only.
@VitWW and @programmerjake Thanks for the feedback, I've modified the RFC to allow trailing whitespace between the opening quote and the first EOL character
I want to be clear that I did not suggest taking the minimum. I suggested using the first line's indentation as the amount to strip, instead. (If some later non-blank line has less indentation than the first line, it should be an error.)
I want to be clear that I did not suggest taking the minimum. I suggested using the first line's indentation as the amount to strip, instead. (If some later non-blank line has less indentation than the first line, it should be an error.)
Same issue really.
Sometimes you do want the first line to be indented.
How about using the first line to indicate the indentation so
let sql = d" \
create table student(
id int primary key,
name text
)
";
would prefix each line with two spaces.
@ssendev I also prefer explicit indentation, but I prefer the last line with closed quote to to indicate the indentation
let sql = d"
create table student(
id int primary key,
name text
)
••••"; // 4 spaces
This is simpler then search all non-empty lines and calculate minimum quantity of whitespaces. We just manually and explicitly mark the indentation. In the "Prior art" it is written that also PHP uses such technique.