I am using this rust code block to handle the json:
fn main() {
let new_tags: String = "[{"code":"DEVELOPER"}]";
}
the compiler shows error invalid suffix code.how to treated the json to literal for dynamic content? I already know to convert static string as literal string like this:
let new_tags: &str = r#"[{"code":"DEVELOPER"}]"#;
this is the debugging info when I am passed json in production code:
could not parse the right json string, the parsed return is Null.
You are confusing yourself with respect to the meaning of escaped strings. In your original code (ignore the unrelated type error)
let new_tags: String = "[{"code":"DEVELOPER"}]";
the compiler has to bail because the " inside the string ("code") stops the parser; what the parser sees is "[{", code ":" DEVELOPER "}]", which makes no sense at all to the compiler. You could escape the inner ", as in "[{\"code\":\"DEVELOPER\"}]" or use a raw string (like you did) as in
let new_tags: &str = r#"[{"code":"DEVELOPER"}]"#;
which causes the parser to see the whole string as one literal (from #" all the way to "#).
But then you confuse yourself again in the line
let tag_str = format!("{}{}{}", "r#\"",new_tags, "\"#");
There is no need to put a literal r#" into the final string; the r#"/"# is only there to disaggregate the actual string (which contains ") from the way the string has to be written down in Rust code (which also uses ").
The JSON parser in serde_json will see the string r#"[{"code":"DEVELOPER"}]"#, which is obviously not valid JSON. serde_json would report the error, but in the line
serde_json::from_str(&*tag_str).unwrap_or_default();
you are supressing the error via the unwrap_or_default call, which gives you an Value::Null.
What you need to do is get rid of the format!-thing. The r#"[{"code":"DEVELOPER"}]"# is already valid JSON.
Related
This question already has answers here:
println! error: expected a literal / format argument must be a string literal
(4 answers)
What does the word "literal" mean?
(11 answers)
Closed last month.
Sorry if this is a noob question.
I've created a .cargo/config.toml file in my project and configured some env variables like so:
[env]
GAME_ZERO_LEVEL = "0"
GAME_ZERO_MULTIPLIER = "0.1"
GAME_ONE_LEVEL = "1"
GAME_ONE_MULTIPLIER = "1.0"
GAME_TWO_LEVEL = "2"
GAME_TWO_MULTIPLIER = "2.5"
GAME_THREE_LEVEL = "3"
GAME_THREE_MULTIPLIER = "5.0"
GAME_FOUR_LEVEL = "4"
GAME_FOUR_MULTIPLIER = "10.0"
I will parse these into u32 and f32.
Anyway, I'm able to fetch these individually with
let value = option_env!("GAME_ZERO_LEVEL").unwrap();
Here is the problem, I want to fetch all env variables in a loop like so:
let env_keys: Vec<&str> = vec![
"GAME_ZERO_LEVEL",
"GAME_ZERO_MULTIPLIER",
"GAME_ONE_LEVEL",
"GAME_ONE_MULTIPLIER",
"GAME_TWO_LEVEL",
"GAME_TWO_MULTIPLIER",
"GAME_THREE_LEVEL",
"GAME_THREE_MULTIPLIER",
"GAME_FOUR_LEVEL",
"GAME_FOUR_MULTIPLIER",
];
env_keys.iter().for_each(|key| {
let value = option_env!(key).unwrap(); //error here
// do some stuff with it
}
But I get the following compile error
rustc: argument must be a string literal
Safe to say, Im pretty confused as my previous understanding was that &str are string literals, and that passing in a &str in a variable doesnt't work. Any help understanding this would be much appreciated!
Note: I cant use std::env::{var, vars}
Slight misconception there &str or more specifically &'static stris the type of string literals but string literals are just the ones you type literally like this: "this is a string literal", or this r"I'm a raw string literal" or even like this r#"I'm a raw string literal which can contain " wherever"#. Everything else is not a literal. Things that are not string literals also can have that type though.
Compare also string literals in Rust by example and the reference
option_env!() evaluates it's contents at compile time so you can't use it with runtime variables like your key.
I'm walking around syntax tree in Go, trying to find all calls to some particular function and then get its string argument (it's a file name, and should be string literal, not any other identifier). I'm succeeded with this, and now I have ast.BasicLit node with Kind == token.STRING, but its value is Go code, not a value of the string that it should have.
I found question that answers how to do reverse transformation - from string to go code it represents: golang: given a string, output an equivalent golang string literal
But I want the opposite - something like eval function (but just for Go string literals).
You can use the strconv.Unquote() to do the conversion (unquoting).
One thing you should be aware of is that strconv.Unquote() can only unquote strings that are in quotes (e.g. start and end with a quote char " or a back quote char `), so you have to manually append that if it's not in quotes.
Example:
fmt.Println(strconv.Unquote("Hi")) // Error: invalid syntax
fmt.Println(strconv.Unquote(`Hi`)) // Error: invalid syntax
fmt.Println(strconv.Unquote(`"Hi"`)) // Prints "Hi"
fmt.Println(strconv.Unquote(`"Hi\x21"`)) // Prints "Hi!"
// This will print 2 lines:
fmt.Println(strconv.Unquote(`"First line\nSecondline"`))
Output (try it on the Go Playground):
invalid syntax
invalid syntax
Hi <nil>
Hi! <nil>
First line
Secondline <nil>
In Swift I can create a String variable such as this:
let s = "Hello\nMy name is Jack!"
And if I use s, the output will be:
Hello
My name is Jack!
(because the \n is a linefeed)
But what if I want to programmatically obtain the raw characters in the s variable? As in if I want to actually do something like:
let sRaw = s.raw
I made the .raw up, but something like this. So that the literal value of sRaw would be:
Hello\nMy name is Jack!
and it would literally print the string, complete with literal "\n"
Thank you!
The newline is the "raw character" contained in the string.
How exactly you formed the string (in this case from a string literal with an escape sequence in source code) is not retained (it is only available in the source code, but not preserved in the resulting program). It would look exactly the same if you read it from a file, a database, the concatenation of multiple literals, a multi-line literal, a numeric escape sequence, etc.
If you want to print newline as \n you have to convert it back (by doing text replacement) -- but again, you don't know if the string was really created from such a literal.
You can do this with escaped characters such as \n:
let secondaryString = "really"
let s = "Hello\nMy name is \(secondaryString) Jack!"
let find = Character("\n")
let r = String(s.characters.split(find).joinWithSeparator(["\\","n"]))
print(r) // -> "Hello\nMy name is really Jack!"
However, once the string s is generated the \(secondaryString) has already been interpolated to "really" and there is no trace of it other than the replaced word. I suppose if you already know the interpolated string you could search for it and replace it with "\\(secondaryString)" to get the result you want. Otherwise it's gone.
I'm starting to like the Swift string formatting since it uses variable names in the string rather than ambiguous formatting tags like "%#"
I want to load a large string from a file that has Swift-style formatting in it (like this)
Now is the time for all good \(who) to come to babble incoherently.
Then I want to feed the contents of that String variable into a statement that lest me replace
\(who)
with the contents of the constant/variable who at runtime.
The code below works with a string constant as the formatting string.
let who = "programmers"
let aString = "Now is the time for all good \(who) to come to babble incoherently."
That code does formatting of a quoted string that appears in-line in my code.
Instead I want something like the code
let formatString = "Now is the time for all good %# to come to babble incoherently."
aString = String(format: formatString, who)
But where I can pass in a Swift-style format string in a constant/variable I read from a file.
Is that possible? I didn't have any luck searching for it since I wasn't exactly sure what search terms to use.
I can always use C-style string formatting and the String class' initWithFormat method if I have to...
I don't think there's a way to do this. String interpolation is implemented via conforming to the StringInterpolationConvertible protocol, and presumably you're hoping to tap into that in the same way you can tap into the methods required by StringLiteralConvertible, a la:
let someString = toString(42)
// this is the method String implements to conform to StringLiteralConvertible
let anotherString = String(stringLiteral: someString)
// anotherString will be "42"
print(anotherString)
Unfortunately, you can't do quite the same trick with StringInterpolationConvertible. Seeing how the protocol works may help:
struct MyString: Printable {
let actualString: String
var description: String { return actualString }
}
extension MyString: StringInterpolationConvertible {
// first, this will get called for each "segment"
init<T>(stringInterpolationSegment expr: T) {
println("Processing segment: " + toString(expr))
actualString = toString(expr)
}
// here is a type-specific override for Int, that coverts
// small numbers into words:
init(stringInterpolationSegment expr: Int) {
if (0..<4).contains(expr) {
println("Embigening \(expr)")
let numbers = ["zeo","one","two","three"]
actualString = numbers[expr]
}
else {
println("Processing segment: " + toString(expr))
actualString = toString(expr)
}
}
// finally, this gets called with an array of all of the
// converted segments
init(stringInterpolation strings: MyString...) {
// strings will be a bunch of MyString objects
actualString = "".join(strings.map { $0.actualString })
}
}
let number = 3
let aString: MyString = "Then shalt thou count to \(number), no more, no less."
println(aString)
// prints "Then shalt thou count to three, no more, no less."
So, while you can call String.init(stringInterpolation:) and String.init(stringInterpolationSegment:) directly yourself if you want (just try String(stringInterpolationSegment: 3.141) and String(stringInterpolation: "blah", "blah")), this doesn't really help you much. What you really need is a facade function that coordinates the calls to them. And unless there's a handy pre-existing function in the standard library that does exactly that which I've missed, I think you're out of luck. I suspect it's built into the compiler.
You could maybe write your own to achieve your goal, but a lot of effort since you'd have to break up the string you want to interpolate manually into bits and handle it yourself, calling the segment init in a loop. Also you'll hit problems with calling the combining function, since you can't splat an array into a variadic function call.
I don't think so. The compiler needs to be able to resolve the interpolated variable at compile time.
I'm not a Swift programmer, specifically, but I think you can workaround it to something pretty close to what you want using a Dictionary and standard string-replacing and splitting methods:
var replacement = [String: String]()
replacement["who"] = "programmers"
Having that, you can try to find the occurrences of "\(", reading what is next and prior to a ")", (this post can help with the split part, this one, with the replacing part), finding it in the dictionary, and reconstructing your string from the pieces you get.
this one works like a charm:
let who = "programmers"
let formatString = "Now is the time for all good %# to come to babble incoherently."
let aString = String(format: formatString, who)
I saw the operator r#"" in Rust but I can't find what it does. It came in handy for creating JSON:
let var1 = "test1";
let json = r#"{"type": "type1", "type2": var1}"#;
println!("{}", json) // => {"type2": "type1", "type2": var1}
What's the name of the operator r#""? How do I make var1 evaluate?
I can't find what it does
It has to do with string literals and raw strings. I think it is explained pretty well in this part of the documentation, in the code block that is posted there you can see what it does:
"foo"; r"foo"; // foo
"\"foo\""; r#""foo""#; // "foo"
"foo #\"# bar";
r##"foo #"# bar"##; // foo #"# bar
"\x52"; "R"; r"R"; // R
"\\x52"; r"\x52"; // \x52
It negates the need to escape special characters inside the string.
The r character at the start of a string literal denotes a raw string literal. It's not an operator, but rather a prefix.
In a normal string literal, there are some characters that you need to escape to make them part of the string, such as " and \. The " character needs to be escaped because it would otherwise terminate the string, and the \ needs to be escaped because it is the escape character.
In raw string literals, you can put an arbitrary number of # symbols between the r and the opening ". To close the raw string literal, you must have a closing ", followed by the same number of # characters as there are at the start. With zero or more # characters, you can put literal \ characters in the string (\ characters do not have any special meaning). With one or more # characters, you can put literal " characters in the string. If you need a " followed by a sequence of # characters in the string, just use the same number of # characters plus one to delimit the string. For example: r##"foo #"# bar"## represents the string foo #"# bar. The literal doesn't stop at the quote in the middle, because it's only followed by one #, whereas the literal was started with two #.
To answer the last part of your question, there's no way to have a string literal that evaluates variables in the current scope. Some languages, such as PHP, support that, but not Rust. You should consider using the format! macro instead. Note that for JSON, you'll still need to double the braces, even in a raw string literal, because the string is interpreted by the macro.
fn main() {
let var1 = "test1";
let json = format!(r#"{{"type": "type1", "type2": {}}}"#, var1);
println!("{}", json) // => {"type2": "type1", "type2": test1}
}
If you need to generate a lot of JSON, there are many crates that will make it easier for you. In particular, with serde_json, you can define regular Rust structs or enums and have them serialized automatically to JSON.
The first time I saw this weird notation is in glium tutorials (old crate for graphics management) and is used to "encapsulate" and pass GLSL code (GL Shading language) to shaders of the GPU
https://github.com/glium/glium/blob/master/book/tuto-02-triangle.md
As far as I understand, it looks like the content of r#...# is left untouched, it is not interpreted in any way. Hence raw string.