Get the file name as a string in Rust - error-handling

I am trying to get the file name of a given string using the following code:
fn get_filename() -> Result<(), std::io::Error> {
let file = "folder/file.text";
let path = Path::new(file);
let filename = path.file_name()?.to_str()?;
println!("{}",filename);
Ok(())
}
But I get this error:
error[E0277]: `?` couldn't convert the error to `std::io::Error`
The original code didn't want to return Result, but that's the only way to use ?. How can I fix this?

Based on Niklas's answer here is the answer:
fn filename() -> Option<()> {
let file = "hey.text";
let path = Path::new(file);
let filename = path.file_name()?.to_str()?;
println!("{}",filename);
None
}

Both .file_name() and .to_str() here return an Option. The usage of the ? operator on the None variant to convert to an Err is an experimental feature (see the NoneError documentation). If you want to use the ? operator, you could consider returning Option<()> here or unwrapping the result by hand.

Related

Unable to replace string inside a String in Kotlin

I am trying to replace a few sub strings inside a string. But my code doesn't seem to work.
val listOfMaleWords = listOf(" him", " he", " his")
val listOfFemaleWords = listOf(" her", " she", " her")
fun modifyIdeaForGender(rawIdea : String, desiredGender : String): String {
var theRawIdea = rawIdea
if (desiredGender == "FEMALE") {
println("desired gender is FEMALE")
listOfMaleWords.forEachIndexed { index, element ->
theRawIdea.replace(element, listOfFemaleWords[index])
}
} else {
println("desired gender is MALE")
listOfFemaleWords.forEachIndexed { index, element ->
theRawIdea.replace(element, listOfMaleWords[index])
}
}
return theRawIdea
}
fun main() {
var sampleString : String = "Tell him, he is special"
println(modifyIdeaForGender(sampleString, "FEMALE"))
}
Expected Output :
"Tell her, she is special"
Current Output :
"Tell him, he is special" // no change
Whats wrong with my code? The current output doesn't replace the string characters at all.
replace returns a new String that you are discarding immediately. It does not mutate theRawIdea itself, so you should assign it back to theRawIdea yourself. For example:
theRawIdea = theRawIdea.replace(element, listOfFemaleWords[index])
Though this would modify theRawIdea as you desire, it wouldn't replace the pronouns correctly. Once it replaces the "him"s with "her"s, it would try to replace the "he"s with "she"s. But note that "he" a substring of "her"! So this would produce:
Tell sher, she is special
This could be fixed by reordering the lists, putting the "he"-"she" pair first, or by using regex, adding \b word boundary anchors around the words:
// note that you should not have spaces before the words if you decide to use \b
val listOfMaleWords = listOf("him", "he", "his")
val listOfFemaleWords = listOf("her", "she", "her")
...
theRawIdea = theRawIdea.replace("\\b$element\\b".toRegex(), listOfFemaleWords[index])
Note that this doesn't account for capitalisation or the fact that changing from female gender pronouns to male ones is inherently broken. Your current code would change all her to him. It would require some more complicated natural language processing to accurately do this task in general.
Taking all that into account, I've rewritten your code with zip:
fun modifyMaleIdeaToFemaleGender(rawIdea : String): String {
var theRawIdea = rawIdea
// if you really want to do the broken female to male case, then this would be
// listOfFemaleWords zip listOfMaleWords
// and the loop below can stay the same
val zipped = listOfMaleWords zip listOfFemaleWords
zipped.forEach { (target, replacement) ->
theRawIdea = theRawIdea.replace("\\b$target\\b".toRegex(), replacement)
}
return theRawIdea
}
You can also use fold to avoid reassigning theRawIdea:
fun modifyIdeaToFemaleGender(rawIdea : String): String {
val zipped = listOfMaleWords zip listOfFemaleWords
return zipped.fold(rawIdea) { acc, (target, replacement) ->
acc.replace("\\b$target\\b".toRegex(), replacement)
}
}
Your code assumes that the replace() method performs an in-place mutation of the string. However, the string with the replaced values are returned by the replace(). So you need to change your code to contain something like:
theRawIdea = theRawIdea.replace(element, listOfFemaleWords[index])
To do this, you will have to use a conventional loop instead of listOfMaleWords.forEachIndexed style looping.

How to get String description/value?

In Kotlin, how to get the raw value of the String?
For example,
val value: String = "Adrian"
Expected result:
"Cannot find value: Adrian"
I am coming from Swift and I know in swift it works like this
let value: String = "Adrian"
print("Cannot find \(string.description): \(value)")
Another example in Swift,
let a: String = "b"
print("\(a.description) = \(a)"
///prints "a = b"
Im guessing a String extension is needed given I read the Kotlin String documentation and seems none of the choices provides the expected result.
A simple problem but I really can't solve it:(
This might help you. For this you have to use Kotlin reflection:
Example:
data class Person(val name:String)
fun main(){
val person = Person("Birju")
val prop = person::name
println("Property Name: ${prop.name}")
println("Property Value: ${prop.get()}")
}
How about
println("value :$value")
You don't need concatination operator(+) to concat strings in kotlin

How to typesafe reduce a Collection of Either to only Right

Maybe a stupid question but I just don't get it.
I have a Set<Either<Failure, Success>> and want to output a Set<Success> with Arrow-kt.
You can map the set like this for right:
val successes = originalSet.mapNotNull { it.orNull() }.toSet()
or if you want the lefts:
val failures = originalSet.mapNotNull { it.swap().orNull() }.toSet()
The final toSet() is optional if you want to keep it as a Set as mapNotNull is an extension function on Iterable and always returns a List
PS: No stupid questions :)
Update:
It can be done avoiding nullables:
val successes = originalSet
.map { it.toOption() }
.filter { it is Some }
.toSet()
We could potentially add Iterable<Option<A>>.filterSome and Iterable<Either<A, B>.mapAsOptions functions.
Update 2:
That last example returns a Set<Option<Success>>. If you want to unwrap the results without using null then one thing you can try is to fold the Set:
val successes = originalSet
.fold(emptySet<Success>()) { acc, item ->
item.fold({ acc }, { acc + it })
}
This last option (unintended pun) doesn't require the use of Option.

kotlin when expression autocast

I would like the following kotlin code to work:
val result: Try<Option<String>> = Success(Some("test"))
val test = when {
result is Success && result.value is Some -> result.value.t // not working
result is Success && result.value is None -> "Empty result"
result is Failure -> "Call failed!"
else -> "no match!"
}
I use the arrow library for the Try and Option monad.
Unfortunately, I can only access the value of the first condition "is Success" and not the second condition "is Some". So, I can only do "result.value", I then get an Option of String.
Am I missing something? This will save me alot of inner ".map" and ".fold" calls.
Update:
I need to cast it first, which is ugly:
result is Success && result.value is Some -> (result.value as Some<String>).t
I tried your example in IntelliJ with Kotlin 1.3.21.
It shows the reason of the problem:
You need to extract the result.value as a variable to make it work. I found the following snippet to solve it
val result: Try<Option<String>> = Success(Some("test"))
val test = when (result) {
is Success -> when(val value = result.value) {
is Some -> value.t
is None -> "None"
}
is Failure -> "Call failed!"
else -> "no match!"
}
I use Kotlin 1.3.x when with declaration syntax.
You may also use Arrow API to get similar result:
val test = result.fold(
ifSuccess = { it.getOrElse { "None" }},
ifFailure = { "Call failed!" }
)
Here you do not need to have the else clause in when.
You can simplify the pattern matching like this:
val test = result
.map { it.getOrElse { "Empty result"} }
.getOrElse { "Call failed!" }
Which is a bit more exhaustive and doesn't require an else alternative
Alternatively, if you don't care about the exception that is thrown you can use toOption on the Try:
val test = result
.toOption()
.getOrElse { "No value!!" }
However, that has some obvious loss of information.
I personally would bubble up the Try instance to the consumer of the result collapsing the inner Option with a .map so that the final result is of type Try<String> and let the consumer handle the error.
However, it depends a lot of the actual context of the problem.

Type in Array changed when Converting Objective-C Class to Swift

I post a request to get some json data and try to convert the data to Model
var newDrugs = Array<DxyDrugInfo>()
newDrugs = self.parseDrugJsonToModels(data) ?? []
private func parseDrugJsonToModels(data: NSData) -> Array<DxyDrugInfo>?{
let json = JSON(data: data)
if let drugsArray = json["data"].arrayObject as? Array<[String : AnyObject]>, success = json["success"].bool where success {
var sortedArray = Array<DxyDrugInfo>()
for drugDic in drugsArray {
let drugInfo = DxyDrugInfo()
drugInfo.setValuesForKeysWithDictionary(drugDic)
sortedArray.append(drugInfo)
}
return sortedArray
}
return nil
}
DxyDrugInfo is a Objective-C Model Class
My question is the sortedArray's type is Array of DxyDrugInfo, and it changed to
when assigned to newDrugs.
I want to convert it to DxyDrugInfo and I also want to what the type is.
Thank you
That type you're looking at, #lvalue [DxyDrugInfo], is "array of DxyDrugInfo". You're already done! The square brackets around a type are shorthand for "array", that is:
[DxyDrugInfo] == Array<DxyDrugInfo>
and #lvalue just means that newDrugs is a variable you can assign to. (It's the opposite of "r-value", which is a value you could assign to an "l-value".)