Kotlin: Modifying (immutable) List through cast, is it legitimate? - collections

As we know the List in Kotlin is immutable i.e. you can't do add and remove as below.
class TempClass {
var myList: List<Int>? = null
fun doSomething() {
myList = ArrayList<Int>()
myList!!.add(10)
myList!!.remove(10)
}
}
But if we cast it to ArrayList as below, the add and remove works.
class TempClass {
var myList: List<Int>? = null
fun doSomething() {
myList = ArrayList<Int>()
(myList!! as ArrayList).add(10)
(myList!! as ArrayList).remove(10)
}
}
I just thought this is odd, as myList is really a List, which is suppose to be immutable. And casting it, allow it to be altered.
Is what done above (casting to Array and modify the content) legitimate, or the language need to improve to disallow that?

There are a few different types of immutability:
One is mentioned from a separate SO answer here.
Readonly - you are NOT supposed to change it (Kotlin's List) but something may (cast to Mutable, or change from Java).
List is just an interface that does not have mutating methods, but you can change the instance if you cast it to MutableList.
Someone then goes on to comment that Kotlin chose to be readonly in order to use Java collections directly, so there is no overhead or conversion in using Java collections.
Kotlin List is readonly, not immutable. Other callers (Java for example) may change the list. Kotlin callers might cast the list and change it. There is no immutable protection.
Original Source: Kotlin and Immutable Collections?

Is it legitimate? Well, yes. There are uses cases in which that would make sense.
Is it a good idea? I think not, especially if you're talking about casting a list that was returned by some external library. The cast will fail if someone actually hands you some List implementations that really is immutable and does not implement MutableList. The fact that at the moment (Kotlin 1.0.2), all of Kotlin's Lists are also MutableLists doesn't mean that every List you'll ever see in your code is also an MutableList.

Right now if you use listOf() you'll get a List with all methods, which mutate the list, throwing java.lang.UnsupportedOperationException:
val list = listOf(1, 2)
val mlist = list as MutableList
mlist.add(3)
This throws:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)

Related

Add element to immutable vector rust

I am trying to create a user input validation function in rust utilising functional programming and recursion. How can I return an immutable vector with one element concatenated onto the end?
fn get_user_input(output_vec: Vec<String>) -> Vec<String> {
// Some code that has two variables: repeat(bool) and new_element(String)
if !repeat {
return output_vec.add_to_end(new_element); // What function could "add_to_end" be?
}
get_user_input(output_vec.add_to_end(new_element)) // What function could "add_to_end" be?
}
There are functions for everything else:
push adds a mutable vector to a mutable vector
append adds an element to the end of a mutable vector
concat adds an immutable vector to an immutable vector
??? adds an element to the end of a immutable vector
The only solution I have been able to get working is using:
[write_data, vec![new_element]].concat()
but this seems inefficient as I'm making a new vector for just one element (so the size is known at compile time).
You are confusing Rust with a language where you only ever have references to objects. In Rust, code can have exclusive ownership of objects, and so you don't need to be as careful about mutating an object that could be shared, because you know whether or not the object is shared.
For example, this is valid JavaScript code:
const a = [];
a.push(1);
This works because a does not contain an array, it contains a reference to an array.1 The const prevents a from being repointed to a different object, but it does not make the array itself immutable.
So, in these kinds of languages, pure functional programming tries to avoid mutating any state whatsoever, such as pushing an item onto an array that is taken as an argument:
function add_element(arr) {
arr.push(1); // Bad! We mutated the array we have a reference to!
}
Instead, we do things like this:
function add_element(arr) {
return [...arr, 1]; // Good! We leave the original data alone.
}
What you have in Rust, given your function signature, is a totally different scenario! In your case, output_vec is owned by the function itself, and no other entity in the program has access to it. There is therefore no reason to avoid mutating it, if that is your goal:
fn get_user_input(mut output_vec: Vec<String>) -> Vec<String> {
// Add mut ^^^
You have to keep in mind that any non-reference is an owned value. &Vec<String> would be an immutable reference to a vector something else owns, but Vec<String> is a vector this code owns and nobody else has access to.
Don't believe me? Here's a simple example of broken code that demonstrates this:
fn take_my_vec(y: Vec<String>) { }
fn main() {
let mut x = Vec::<String>::new();
x.push("foo".to_string());
take_my_vec(x);
println!("{}", x.len()); // E0382
}
The expression x.len() causes a compile-time error, because the vector x was moved into the function argument and we don't own it anymore.
So why shouldn't the function mutate the vector it owns now? The caller can't use it anymore.
In summary, functional programming looks a bit different in Rust. In other languages that have no way to communicate "I'm giving you this object" you must avoid mutating values you are given because the caller may not expect you to change them. In Rust, who owns a value is clear, and the argument reflects that:
Is the argument a value (Vec<String>)? The function owns the value now, the caller gave it away and can't use it anymore. Mutate it if you need to.
Is the argument an immutable reference (&Vec<String>)? The function doesn't own it, and it can't mutate it anyway because Rust won't allow it. You could clone it and mutate the clone.
Is the argument a mutable reference (&mut Vec<String>)? The caller must explicitly give the function a mutable reference and is therefore giving the function permission to mutate it -- but the function still doesn't own the value. The function can mutate it, clone it, or both -- it depends what the function is supposed to do.
If you take an argument by value, there is very little reason not to make it mut if you need to change it for whatever reason. Note that this detail (mutability of function arguments) isn't even part of the function's public signature simply because it's not the caller's business. They gave the object away.
Note that with types that have type arguments (like Vec) other expressions of ownership are possible. Here are a few examples (this is not an exhaustive list):
Vec<&String>: You now own a vector, but you don't own the String objects that it contains references to.
&Vec<&String>: You are given read-only access to a vector of string references. You could clone this vector, but you still couldn't change the strings, just rearrange them, for example.
&Vec<&mut String>: You are given read-only access to a vector of mutable string references. You can't rearrange the strings, but you can change the strings themselves.
&mut Vec<&String>: Like the above but opposite: you are allowed to rearrange the string references but you can't change the strings.
1 A good way to think of it is that non-primitive values in JavaScript are always a value of Rc<RefCell<T>>, so you're passing around a handle to the object with interior mutability. const only makes the Rc<> immutable.

Rust, std::cell::Cell - get immutable reference to inner data

Looking through the documentation for std::cell::Cell, I don't see anywhere how I can retrieve a non-mutable reference to inner data. There is only the get_mut method: https://doc.rust-lang.org/std/cell/struct.Cell.html#method.get_mut
I don't want to use this function because I want to have &self instead of &self mut.
I found an alternative solution of taking the raw pointer:
use std::cell::Cell;
struct DbObject {
key: Cell<String>,
data: String
}
impl DbObject {
pub fn new(data: String) -> Self {
Self {
key: Cell::new("some_uuid".into()),
data,
}
}
pub fn assert_key(&self) -> &str {
// setup key in the future if is empty...
let key = self.key.as_ptr();
unsafe {
let inner = key.as_ref().unwrap();
return inner;
}
}
}
fn main() {
let obj = DbObject::new("some data...".into());
let key = obj.assert_key();
println!("Key: {}", key);
}
Is there any way to do this without using unsafe? If not, perhaps RefCell will be more practical here?
Thank you for help!
First of, if you have a &mut T, you can trivially get a &T out of it. So you can use get_mut to get &T.
But to get a &mut T from a Cell<T> you need that cell to be mutable, as get_mut takes a &mut self parameter. And this is by design the only way to get a reference to the inner object of a cell.
By requiring the use of a &mut self method to get a reference out of a cell, you make it possible to check for exclusive access at compile time with the borrow checker. Remember that a cell enables interior mutability, and has a method set(&self, val: T), that is, a method that can modify the value of a non-mut binding! If there was a get(&self) -> &T method, the borrow checker could not ensure that you do not hold a reference to the inner object while setting the object, which would not be safe.
TL;DR: By design, you can't get a &T out of a non-mut Cell<T>. Use get_mut (which requires a mut cell), or set/replace (which work on a non-mut cell). If this is not acceptable, then consider using RefCell, which can get you a &T out of a non-mut instance, at some runtime cost.
In addition to to #mcarton answer, in order to keep interior mutability sound, that is, disallow mutable reference to coexist with other references, we have three different ways:
Using unsafe with the possibility of Undefined Behavior. This is what UnsafeCell does.
Have some runtime checks, involving runtime overhead. This is the approach RefCell, RwLock and Mutex use.
Restrict the operations that can be done with the abstraction. This is what Cell, Atomic* and (the unstable) OnceCell (and thus Lazy that uses it) does (note that the thread-safe types also have runtime overhead because they need to provide some sort of locking). Each provides a different set of allowed operations:
Cell and Atomic* do not let you to get a reference to the contained value, and only replace it as whole (basically, get() and set, though convenience methods are provided on top of these, such as swap()). Projection (cell-of-slice to slice-of-cells) is also available for Cell (field projection is possible, but not provided as part of std).
OnceCell allows you to assign only once and only then take shared reference, guaranteeing that when you assign you have no references and while you have shared references you cannot assign anymore.
Thus, when you need to be able to take a reference into the content, you cannot choose Cell as it was not designed for that - the obvious choice is RefCell, indeed.

Why has Kotlins SortedMap no .forEachIndexed() function?

Kotlins SortedMap is "a map that further provides a total ordering on its keys."
As a result, it should be indexable. However, this extension doesn't exist
`sortedMap.forEachIndexed()`
Why not? Am i overlooking something? Is it performance reasons? Didn't anyone bother?
(Yes, i know, i could use a List<Pair<Key, Value>>, but that's doesn't feel like the "intuitive" structure for my usecase, a map fits much better)
Most of the things that have a forEachIndexed get it either from Iterable or have it as an extension function. Map does not, but one of its properties, the entries is actually a Set, which does have forEachIndexed because it inherits from Collection (which inherits from Iterable).
That means that you can do something like this:
map.entries.forEachIndexed { index, (key, value) ->
//do stuff
}
The reason I've added this to the already existing asIterable().forEachIndex answer, is because asIterable() creates a new object.
forEachIndexed is an extension function on all sorts of Arrays and also Iterable. SortedMap is unrelated to those types, so you can't call forEachIndexed on it.
However, SortedMap does have asIterable (inherited from Map), which converts it to an Iterable. After that, you can access forEachIndexed:
someSortedMap.asIterable().forEachIndex { index, entry ->
// ...
}
However, the newer extension function onEachIndexed are declared on Maps. Unlike forEachIndexed, this also returns the map itself.

How to obtain a KType in Kotlin?

I'm experimenting with the reflection functionality in Kotlin, but I can't seem to understand how to obtain a KType value.
Suppose I have a class that maps phrases to object factories. In case of ambiguity, the user can supply a type parameter that narrows the search to only factories that return that type of object (or some sub-type).
fun mapToFactory(phrase: Phrase,
type: KType = Any::class): Any {...}
type needs to accept just about anything, including Int, which from my experience seems to be treated somewhat specially. By default, it should be something like Any, which means "do not exclude any factories".
How do I assign a default value (or any value) to type?
From your description, sounds like your function should take a KClass parameter, not a KType, and check the incoming objects with isSubclass, not isSubtype.
Types (represented by KType in kotlin-reflect) usually come from signatures of declarations in your code; they denote a broad set of values which functions take as parameters or return. A type consists of the class, generic arguments to that class, and nullability. The problem with types at runtime on JVM is that because of erasure, it's impossible to determine the exact type of a variable of a generic class. For example if you have a list, you cannot determine the generic type of that list at runtime, i.e. you cannot differentiate between List<String> and List<Throwable>.
To answer your initial question though, you can create a KType out of a KClass with createType():
val type: KType = Any::class.createType()
Note that if the class is generic, you need to pass type projections of generic arguments. In simple cases (all type variables can be replaced with star projections), starProjectedType will also work. For more info on createType and starProjectedType, see this answer.
Since Kotlin 1.3.40, you can use the experimental function typeOf<T>() to obtain the KType of any type:
val int: KType = typeOf<Int>()
In contrast to T::class.createType(), this supports nested generic arguments:
val listOfString: KType = typeOf<List<String>>()
The typeOf<T>() function is particularly useful when you want to obtain a KType from a reified type parameter:
inline fun <reified T> printType() {
val type = typeOf<T>()
println(type.toString())
}
Example usage:
fun main(args: Array<String>) {
printType<Map<Int, String>>()
// prints: kotlin.collections.Map<kotlin.Int, kotlin.String>
}
Since this feature is still in experimental status, you need to opt-in with #UseExperimental(ExperimentalStdlibApi::class) around your function that uses typeOf<T>(). As the feature becomes more stable (possibly in Kotlin 1.4), this can be omitted. Also, at this time it is only available for Kotlin/JVM, not Kotlin/Native or Kotlin/JS.
See also:
Release announcement
API Doc (very sparse currently)

Is it possible to declare a tuple struct whose members are private, except for initialization?

Is it possible to declare a tuple struct where the members are hidden for all intents and purposes, except for declaring?
// usize isn't public since I don't want users to manipulate it directly
struct MyStruct(usize);
// But now I can't initialize the struct using an argument to it.
let my_var = MyStruct(0xff)
// ^^^^
// How to make this work?
Is there a way to keep the member private but still allow new structs to be initialized with an argument as shown above?
As an alternative, a method such as MyStruct::new can be implemented, but I'm still interested to know if its possible to avoid having to use a method on the type since it's shorter, and nice for types that wrap a single variable.
Background
Without going into too many details, the only purpose of this type is to wrap a single type (a helper which hides some details, adds some functionality and is optimized away completely when compiled), in this context it's not exactly exposing hidden internals to use the Struct(value) style initializing.
Further, since the wrapper is zero overhead, its a little misleading to use the new method which is often associated with allocation/creation instead of casting.
Just as it's convenient type (int)v or int(v), instead of int::new(v), I'd like to do this for my own type.
It's used often, so the ability to use short expression is very convenient. Currently I'm using a macro which calls a new method, its OK but a little awkward/indirect, hence this question.
Strictly speaking this isn't possible in Rust.
However the desired outcome can be achieved using a normal struct with a like-named function (yes, this works!)
pub struct MyStruct {
value: usize,
}
#[allow(non_snake_case)]
pub fn MyStruct(value: usize) -> MyStruct {
MyStruct { value }
}
Now, you can write MyStruct(5) but not access the internals of MyStruct.
I'm afraid that such a concept is not possible, but for a good reason. Each member of a struct, unless marked with pub, is admitted as an implementation detail that should not raise to the surface of the public API, regardless of when and how the object is currently being used. Under this point of view, the question's goal reaches a conundrum: wishing to keep members private while letting the API user define them arbitrarily is not only uncommon but also not very sensible.
As you mentioned, having a method named new is the recommended approach of doing that. It's not like you're compromising code readability with the extra characters you have to type. Alternatively, for the case where the struct is known to wrap around an item, making the member public can be a possible solution. That, on the other hand, would allow any kind of mutations through a mutable borrow (thus possibly breaking the struct's invariants, as mentioned by #MatthieuM). This decision depends on the intended API.

Resources